目录

CVE-2022-41852 Apache Commons JXPath RCE

前言

最近在推特上看到有详情爆出来,就来分析学习一下

环境

引入jxpath依赖即可

1
2
3
4
5
<dependency>
    <groupId>commons-jxpath</groupId>
    <artifactId>commons-jxpath</artifactId>
    <version>1.3</version>
</dependency>

漏洞分析

先来弹个计算器,利用的是ClassPathXmlApplicationContext加载恶意xml触发计算器,这里有个关键点在于ClassPathXmlApplicationContext.new这个.new,既然能够触发xml肯定就是调用了构造函数但是为什么要在这里写个.new呢,直接调试看一下

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/f9950227-5dff-10f5-2681-e82a1cf2a9f6.png

1
2
3
4
5
6
7
8
9
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
    <constructor-arg>
      <list>
        <value>calc</value>
      </list>
    </constructor-arg>
  </bean>
</beans>

先进入到this.compileExpression中看看做了哪些操作

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/36790f01-2a9c-94b5-a3ba-00ea07fe9123.png

org.apache.commons.jxpath.ri.JXPathContextReferenceImpl#compileExpression中,compiled是一个hashmap类型且为空,先尝试从compiled取出xpath的value值肯定是为空的,接着进入到parseExpression

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/2ac6d4f7-1e17-5c27-f7cd-08bd6e17df5d.png

可以看到自行解析compiled中没有的键值对然后赋值给expr,然后返回将键值对赋值给compiled在返回expr

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/9e3e8d7e-2827-a7d3-1f97-40bcd8dae569.png

接着进入到this.getValue中,触发构造函数的地方应该就是在这里了,跟进expr.computeValue

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/0e169049-160c-ab34-4cbb-fae3a53e83bd.png

org.apache.commons.jxpath.ri.compiler.ExtensionFunction#computeValue中,先是将expr中的args强制转换赋值给parameters,可以看到在进行function.invoke的时候function为ClassPathXmlApplicationContext的构造函数,而它是在context.getRootContext().getFunction中进行获取的,先跟进看看是怎么转换的

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/f8359592-1c07-f31f-7724-99987ee00072.png

一路跟进到org.apache.commons.jxpath.PackageFunctions#getFunction中,先后进入多次lookupMethod函数,应该为通过nameparameters进行匹配获取对应的函数,跟进去看一下具体是怎么样操作的

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/014b2f55-45df-6819-842a-17a87603473b.png

先强制类型转换获取parameters的类型,然后进行遍历取出其中的值,接着通过targetClass.getMethod(name, types)获取其函数,但是ClassPathXmlApplicationContext肯定不在String类中所以结果为null,最后在通过for循环遍历targetClass中的函数进行匹配

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/474eed74-204b-163f-82e2-79a9f8173295.png

经过几次lookupMethod后,截取最后一个.前后的值并分别赋值给classNamemethodName,接着通过Class.forName加载className也就是ClassPathXmlApplicationContext,然后判断methodName的值是否为new,是的话获取其构造函数,否的话获取其静态函数,然后返回值。也就是说该漏洞不仅可以通过构造函数触发,还可以通过public静态函数触发

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/f6f183a7-9874-0844-6cbc-81972971f3c4.png

再来看看function.invoke,并不是常说的反射调用,而是jxpath自己写的的一个invoke调用函数,最后在ConstructorFunction#invoke中触发漏洞

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/b2fc944f-9015-8630-c387-f177dbf04e7e.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/55d86b61-799c-0644-6d5b-2f5090b35943.png

通过javax.naming.InitialContext.doLookup("ldap://127.0.0.1:8899/")来测试一下静态方法调用,nc监听,最后在MethodFunction#invoke中通过反射调用

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/216db6da-76dd-404e-b2e5-55ba2a3e6c29.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/a679c56f-0d9a-81b7-8258-97f87d97720a.png

小结

总结下来就是可以触发任意类的构造函数和public静态函数,可以根据这两个点找gadget,在看的时候经过MoonBack师傅的提醒,该洞还可以链式调用,相应的gadget的范围就更大了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void aaa(){

     System.out.println("1");
     return "2";
}
public static void bbb(String asd){

     System.out.println(asd);
}

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/782dba01-cc9e-8762-3ea6-14dc45cad315.png

参考连接

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