XMLdecoder安全漏洞分析
Posted 雷神众测
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XMLdecoder安全漏洞分析相关的知识,希望对你有一定的参考价值。
No.1
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
No.2
前言
之前看到关于xmldecoder的解析流程分析文章然后我就想着自己也跟一下,我测试的java版本是1.8.0_171。
No.3
测试代码
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.beans.XMLDecoder;
public class Xmldecoder {
public static void XMLDecode_Deserialize(String path) throws Exception {
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
XMLDecoder xd = new XMLDecoder(bis);
xd.readObject();
xd.close();
}
public static void main(String[] args){
//XMLDecode Deserialize Test
String path = "/Users/l1nk3r/Desktop/poc.xml";
try {
XMLDecode_Deserialize(path);
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="2">
<void index="0">
<string>open</string>
</void><void index="1"><string>/Applications/Calculator.app</string>
</void>
</array>
<void method="start" />
</object>
</java>
No.4
前置知识
这里我选择在 java.lang.ProcessBuilder 接收 array 类型数据传入的地方下个断点。
可以看到整个解析流程涉及到的类和构造方法大概就是如下所示,这里我们看到一个在 package:com.sun.beans.decoder 中 DocumentHandler 类中 parse 解析了,我们的传入的xml文件。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/14fc2c2236844a81a3fd1a9352cf2284.jpg)
这里可以跟进一下 DocumentHandler 类,这个类继承了 DefaultHandler 。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/e80cc96d5c164b52849816ac2c6bfb51.jpg)
跟进一下 DefaultHandler 类,我们可以知道它是使用 sax 来解析 xml 的默认 handler ,而且代码里来看它主要实现了 EntityResolver , DTDHandler , ContentHandler , ErrorHandler 这四个 handler 。
package org.xml.sax.helpers;
import java.io.IOException;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.DTDHandler;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
回过头来看 DocumentHandler 这个类,在这个类的构造方法中,我们看到了 XMLDecoder 对每种支持的标签都实现了一个继承与 ElementHandler 的类。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/fc942b47432d4d1da7f39ff1a7ad5cca.jpg)
所以从目前来看 DocumentHandler 这个构造函数主要的作用就是创建各个标签对应的 ElementHandler 并进行调用。当然关于 DocumentHandler 类因为是继承了 DefaultHandler 类,这一点我们前面聊到了,而 DefaultHandler 类中解析xml的工作主要交付给了 ContentHandler 接口,所以我们可以借鉴一下这个接口是怎么个解析xml内容的,这里直接借鉴一下@fnmsd师傅写的内容,因为我觉得我自己写的话没有他写的这么的棒。
startElement
处理开始标签,包括属性的添加
DocumentHandler:XML解析处理过程中参数包含命名空间URL、标签名、完整标签名、属性列表。根据完整标签名创建对应的ElementHandler并添加相关属性,继续调用其startElement。
ElementHandler: 除了array标签以外,都无操作。
endElement
结束标签处理函数
DocumentHandler: 调用对应ElementHandler的endElement函数,并将当前ElementHandler回溯到上一级的ElementHandler。
ElementHandler: 没看有重写的,都是调用抽象类ElementHandler的endElement函数,判断是否需要向parent写入参数和是否需要注册标签对象ID。
characters
DocumentHandler: 标签包裹的文本内容处理函数,比如处理java.lang.ProcessBuilder包裹的文本内容就会从这个函数走。函数中最终调用了对应ElementHandler的addCharacter函数。
addCharacter
ElementHandler: ElementHandler里的addCharacter只接受接种空白字符(空格
),其余的会抛异常,而StringElementHandler中则进行了重写,会记录完整的字符串值。
addAttribute
ElementHandler: 添加属性,每种标签支持的相应的属性,出现其余属性会报错。
getContextBean
ElementHandler: 获取操作对象,比如method标签在执行方法时,要从获取上级object/void/new标签Handler所创建的对象。该方法一般会触发上一级的getValueObject方法。
getValueObject
ElementHandler: 获取当前标签所产生的对象对应的ValueObject实例。具体实现需要看每个ElementHandler类。
isArgument
ElementHandler: 判断是否为上一级标签Handler的参数。
addArgument
ElementHandler: 为当前级标签Handler添加参数。
XMLDecoder相关的其它
两个成员变量,在类的实例化之前,通过对parent的调用进行增加参数。
parent
最外层标签的ElementHandler的parent为null,而后依次为上一级标签对应的ElementHandler。
owner
ElementHandler: 固定owner为所属DocumentHandler对象。
DocumentHandler: owner固定为所属XMLDecoder对象。
而sax解析xml文件中的单个node节点(根节点)的流程在 DefaultHandler 中一般是三步走: startElement->characters->endElement 。
针对node节点(非根)的解析流程在中是四步走 DefaultHandler 中一般是四步走: startElement->characters->endElement->characters。
所以总结来看本质上最有关系的还是这三个构造方法 startElement、characters、endElement 。
然后,我们可以看看 DocumentHandler 中的这些 ElementHandler 的继承关系,这里重点表扬一下 idea 的 uml 的功能,使用方法很简单,选择一个你想要看的方法,右键选择 Diagrams 。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/61587a66fe144bb2a0af585f07dfcec0.jpg)
然后每次选择 add Class to Diagram 就可以增加你要看的相关类。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/2c870184d1324e069460685a040ccd54.jpg)
最后就生成了下面这张图。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/8e0017dd62964838b648b33112685f49.jpg)
而关于xmldecoder对应的xml文件语法书写如下所示:
每个元素表示方法调用。
“对象”标签表示一个表达式 ,其值将用作封闭元素的参数。
“void”标签表示将被执行的语句 ,但其结果将不会用作封闭方法的参数。
包含元素的元素使用这些元素作为参数,除非它们具有标签:“void”。
方法的名称由“method”属性表示。
XML的标准“id”和“idref”属性用于对前面的表达式进行引用,以便处理对象图形中的循环。
“类”属性用于明确指定静态方法或构造函数的目标; 其值是该类的完全限定名称。
如果没有由“class”属性定义目标,则使用“void”标签的元素将使用外部上下文作为目标来执行。
Java的String类被特别处理,并写入
Hello,world ,其中使用UTF-8字符编码将字符串的字符转换为字节。
No.5
流程跟进
这里跟进一下上面的那些基础知识分析过的内容,首先按照我们的了解来看,针对xml文件的解析最早应该是使用 DefultHandler 中 startElement ,我们在这里下个断点。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/98f6b7c9939443498f8427f6e8fe7c20.jpg)
回过头来看一下xml文件内容,最开始的节点叫java,所以这里解析出来对应的对应的 handler 是 JavaElementHandler 。
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="2">
<void index="0">
<string>open</string>
</void>
<void index="1">
<string>/Applications/Calculator.app</string>
</void>
</array>
<void method="start" />
</object>
</java>
而 JavaElementHandler 中的 addAttribute 构造函数的作用就是添加相应的属性,比如 version 的属性是版本信息这里是 1.8.0_131 ,而另一个就是 class 这里是 java.beans.XMLDecocer 。
public void addAttribute(String var1, String var2) {if (!var1.equals("version")) {if (var1.equals("class")) {this.type = this.getOwner().findClass(var2);
} else {super.addAttribute(var1, var2);
}
}
所以开始第一次 addAttribute 的结果是 version 和它对应的值,当然这个解析过程不是一次,单单是下面内容就会执行两次解析过程。
java version="1.8.0_131" class="java.beans.XMLDecoder"
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/db6cdcd660324518ad54c59c3b281969.jpg)
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/4a66f467424a41d18983684f0ef7ab05.jpg)
当然关键不在这,我想知道为什么能够执行 java.lang.ProcessBuilder 这个类,从前面debug过程来看,如果。startElement 解析道这里处理之后对应的 handler 应该是 ObjectElementHandler 。
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="2">
<void index="0">
<string>open</string>
</void><void index="1"><string>/Applications/Calculator.app</string>
</void>
</array>
<void method="start" />
</object>
在这里下一个断点,果然进来了, ObjectElementHandler 里面的 addAttribute 方法没有能够解析 class 的选项。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/e3b79a8a6ac14c61a93f2698dd7067c6.jpg)
但是由于 ObjectElementHandler 继承了 NewElementHandler ,所以这里便使用 NewElementHandler 中的 addAttribute 方法来处理,这里自然有了能够处理 class 的能力了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/5551d8b12944403788da8a8f579d4fae.jpg)
这里的 type 自然就是名字是 java.lang.ProcessBuilder 的类。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/a114f60a87ed4bc2a9c223d2afb7d90f.jpg)
这里处理完了之后就会继续调用 ArrayElementHandler 处理 array 的内容了,然后调用 VoidElementHandler 处理 void 标签的内容,而 VoidElementHandler 也是继承来自于 ObjectElementHandler ,自然而然也是回到了 ObjectElementHandler 中的 addAttribute 中了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/7887a27eb6804149a37632f17af715fa.jpg)
这里我就下了两个断点,我们可以看到 void 标签中的 index 和 method 处理过程自然都进入到了这里。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/647eb49d9d234cfe85602c4cec0650d4.jpg)
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/93be8002fe974224af0c9914549e5258.jpg)
当然前面过程 void 标签处理完之后,首先会进入 DocumentHandler 的 endElement 方法进行处理,而这个方法中调用了 ElementHandler 中的 endElement 构造方法。
public void endElement(String var1, String var2, String var3) {
try {
this.handler.endElement();
} catch (RuntimeException var8) {
this.handleException(var8);
} finally {
this.handler = this.handler.getParent();
}
}
而在 endElement 构造方法中调用了 getValueObject ,这个方法设置了在 endElement 中被设置成了一个抽象类,我们通过在 java.lang.ProcessBuilder 处下一个断点,可以看到自然进到了 NewElementHandler 中的 getValueObject 了。
public void endElement() {
ValueObject var1 = this.getValueObject();if (!var1.isVoid()) {if (this.id != null) {this.owner.setVariable(this.id, var1.getValue());
}
if (this.isArgument()) {if (this.parent != null) {this.parent.addArgument(var1.getValue());
} else {this.owner.addObject(var1.getValue());
}
}
}
}
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/55d820ebe67f4e56bb0213eb0fb2c48d.jpg)
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/0dbf112602714d789eb78330c53c8027.jpg)
再回顾一下继承关系,这里我放大了一下这部分内容。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/61eb69e7598e41db845d2bcea9a1b62b.jpg)
上面的 NewElementHandler 中的构造方法 getValueObject 在 ObjectElementHandler 、 ArrayElementHandler 、 MethodElementHandler 中分别被重写了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/0932679f8a764783acf8ba4a86f886d6.jpg)
因为我们 payload 里面关于命令执行使用的是 object 标签,所以自然就进来到了 ObjectElementHandler 中,然后这里有调用了 getContextBean 方法,而根据继承关系,这个方法调用的是 NewElementHandler 中的 getContentBean 方法
protected final Object getContextBean() {return this.type != null ? this.type : super.getContextBean();
}
而 NewElementHandler 中的 getContextBean 方法又是因为继承关系调用了来自于 ElementHandler 中的 getContextBean 方法。
protected Object getContextBean() {if (this.parent != null) {
ValueObject var2 = this.parent.getValueObject();if (!var2.isVoid()) {return var2.getValue();
} else {throw new IllegalStateException("The outer element does not return value");
}
} else {
Object var1 = this.owner.getOwner();if (var1 != null) {return var1;
} else {throw new IllegalStateException("The topmost element does not have context");
}
}
而这里的 getValueObjcet 操作根据我的断点执行了两次。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/010b1d3ee1af4288b2cd4e7204238a5d.jpg)
我们可以看一下结果,第一次进入 NewElementHandler 中进行 getValueObject 实际上在 type 里面是没有 class 的值的。而第二次进入 NewElementHandler 中进行 getValueObject 操作的时候 type 是取到相关的 class 的值。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/f1b2dd4b27a54f49a7aef443f359cb42.jpg)
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/4b420d1744fc4100bf771826f68c3459.jpg)
那么根据上面所说这里势必会进入到 ObjectElementHandler 中的 getValueHandler 方法进行处理,而在这个方法的最后是调用了 Expression 中的 getValue 方法,这里可以看到我们要执行的命令以及 class 对象都已经传入了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/9ceba8d54bbe492885f7fbe98ed5562f.jpg)
而进一步动态调试我们可以看到 Expression 中的 getValue 已经返回我们需要的命令执行对象类,以及命令参数了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/54167b7f2e4c4c10bdaeb0fcc2700624.jpg)
然后会重新回到 Void 标签对应的 getValueObject 函数,由于 ViodElementHandler 继承 ObjectElementHandler ,而我们这里设置的 method 等于 start ,因此这里通过 Expression 的反射机制调用了 start 函数,所以到这里自然命令执行了。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/dcaa4d8c54c44fb88405660175b70944.jpg)
这个是从师傅文章里看到的小贴士,自己跟下来之后确实是这样
PS: 虽然ObjectElementHandler继承自NewElementHandler,但是其重写了getValueObject函数,两者是使用不同方法创建类的实例的。
再PS: 其实不加java标签也能用,但是没法包含多个对象了。
然后再补充一下@fnmsd师傅画的流程图
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/5ee6bcb7c770433aa052809109b64bf7.jpg)
No.6
补丁
Oracle关于这个xmldecoder造成的漏洞的CVE编号分别是CVE-2017-3506、CVE-2017-10271、CVE-2019-2725。最早关于CVE-2017-3506的补丁只是根据Object标签进行了限制。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/3fc9a28c7b1846ec892d2802fef4a75a.jpg)
而根据我们前面的继承关系可以讲 object 替换成 void 即可,它们实际上是不受影响的,因此便出现了CVE-2017-10271,而针对CVE-2017-10271的补丁限定了所有具有执行的节点。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/6f2c3acea81c418ab7afb7e0ab16ed30.jpg)
但这次 CVE-2019-2725 主要是 class 标签,class 标签可代替 object 标签来生成对象,因此这次漏洞本质还是xmldecoder的问题,而补丁也是针对class标签来处理的。
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/ea15f2f0bcbe423c9d6b2bdd8017e5ee.jpg)
No.7
修复建议
1、代码层防护。使用白名单,参考修复部分代码如下:
![XMLdecoder安全漏洞分析](https://image.cha138.com/20210330/75155687ae9d429fba81895e669742ab.jpg)
2、采用第三方防护设备进行防护,如waf、云waf等。
No.8
招聘启事
安全招聘
————————
公司:安恒信息
岗位:Web安全 安全研究员
部门:安服战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部),广州
岗位剩余编制:3人
工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…
【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案
【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)
【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;
岗位:安全红队武器自动化攻城狮
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)
【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。
【岗位要求】
1.熟练使用Python、java、c/c++等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3:熟悉域安全以及内网横向渗透、常见web等漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。
设计师
————————
【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。
【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;精通photoshop/illustrator/coreldrew/等设计制作软件;
3、有品牌传播、产品设计或新媒体视觉工作经历;
【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽
简历投递至 strategy@dbappsecurity.com.cn
【工作地点】
杭州、广州
简历投递至 strategy@dbappsecurity.com.cn
安全服务工程师/渗透测试工程师
工作地点:新疆
1、掌握渗透测试原理和渗透测试流程,具备2年以上渗透测试工作经验,能够独立完成渗透测试方案和测试报告;
2、熟悉风险评估、安全评估;
3、熟练掌握各类渗透工具,如Sqlmap、Burpsuite、AWVS、Appscan、Nmap、Metasploit、Kali等;
4、熟练掌握Web渗透手法,如SQL注入、XSS、文件上传等攻击技术;
5、至少熟悉一种编程语言(php/java/python),能够独立编写poc者优先;
6、具有良好的沟通能力和文档编写能力,动手能力强;
7、对工作有热情,耐心、责任心强,具备沟通能力和良好的团队意识;
8、加分项:有安全开发经验/可进行代码审计工作;
9、加分项:有安全和网络相关证书,如CISSP、CISA、CISP 、CCNP、CCIE等认证者;
岗位职责:
1、参与安全服务项目实施,其中包括渗透测试与安全评估,代码审计,安全培训,应急响应;
2、独立完成安全服务项目报告编写,输出安全服务实施报告;
专注渗透测试技术
全球最新网络攻击技术
END
以上是关于XMLdecoder安全漏洞分析的主要内容,如果未能解决你的问题,请参考以下文章