Java 反序列化之 XStream 反序列化
0x01 XStream 基础
XStream 简介
XStream 是一个简单的基于 Java 库,Java 对象序列化到 XML,反之亦然(即:可以轻易的将 Java 对象和 XML 文档相互转换)。
使用 XStream 实现序列化与反序列化
下面看下如何使用 XStream 进行序列化和反序列化操作的。
(资料图)
先定义接口类
IPerson.java
public interface IPerson { void output(); }
接着定义 Person 类实现前面的接口:
public class Person implements IPerson { String name; int age; public void output() { System.out.print("Hello, this is " + this.name + ", age " + this.age); } }
XStream 序列化是调用XStream.toXML()
来实现的:
public class Serialize { public static void main(String[] args) { Person p = new Person(); p.age = 6; p.name = "Drunkbaby"; XStream xstream = new XStream(new DomDriver()); String xml = xstream.toXML(p); System.out.println(xml); } }
XStream 反序列化是用过调用XStream.fromXML()
来实现的,其中获取 XML 文件内容的方式可以通过Scanner()
或FileInputStream
都可以:
Deserialize.java
import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Scanner; public class Deserialize { public static void main(String[] args) throws FileNotFoundException { // String xml = new Scanner(new File("person.xml")).useDelimiter("\\Z").next(); FileInputStream xml = new FileInputStream("G:\\OneDrive - yapuu\\Java安全学习\\JavaSecurityLearning\\JavaSecurity\\XStream\\XStream\\XStream-Basic\\src\\main\\java\\person.xml"); XStream xstream = new XStream(new DomDriver()); Person p = (Person) xstream.fromXML(xml); p.output(); } }
XStream 几个部分
XStream 类图,参考XStream 源码解析:
主要分为四个部分:
【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】
① 网安学习成长路径思维导图 ② 60+网安经典常用工具包 ③ 100+SRC漏洞分析报告 ④ 150+网安攻防实战技术电子书 ⑤ 最权威CISSP 认证考试指南+题库 ⑥ 超1800页CTF实战技巧手册 ⑦ 最新网安大厂面试题合集(含答案) ⑧ APP客户端安全检测指南(安卓+IOS)
MarshallingStrategy 编码策略
marshall : object->xml 编码
unmarshall : xml-> object 解码
两个重要的实现类:
com.thoughtworks.xstream.core.TreeMarshaller
: 树编组程序调用 Mapper 和 Converter 把 XML 转化成 Java 对象
其中的 start 方法开始编组
其中调用了this.convertAnother(item)
方法
convertAnother 方法的作用是把 XML 转化成 Java 对象。
Mapper 映射器
简单来说就是通过 mapper 获取对象对应的类、成员、Field 属性的 Class 对象,赋值给 XML 的标签字段。
Converter 转换器
XStream 为 Java 常见的类型提供了 Converter 转换器。转换器注册中心是 XStream 组成的核心部分。
转换器的职责是提供一种策略,用于将对象图中找到的特定类型的对象转换为 XML 或将 XML 转换为对象。
简单地说,就是输入 XML 后它能识别其中的标签字段并转换为相应的对象,反之亦然。
转换器需要实现 3 个方法,这三个方法分别是来自于Converter
类以及它的父类ConverterMatcher
canConvert 方法:告诉 XStream 对象,它能够转换的对象;
marshal 方法:能够将对象转换为 XML 时候的具体操作;
unmarshal 方法:能够将 XML 转换为对象时的具体操作;
具体参考:http://x-stream.github.io/converters.html
这里告诉了我们针对各种对象,XStream 都做了哪些支持。
EventHandler 类
EventHandler 类为动态生成事件侦听器提供支持,这些侦听器的方法执行一条涉及传入事件对象和目标对象的简单语句。
EventHandler 类是实现了 InvocationHandler 的一个类,设计本意是为交互工具提供 beans,建立从用户界面到应用程序逻辑的连接。
EventHandler 类定义的代码如下,其含有 target 和 action 属性,在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()
的函数调用链中,会将前面两个属性作为类方法和参数继续反射调用:
public class EventHandler implements InvocationHandler { private Object target; private String action; ... public Object invoke(final Object proxy, final Method method, final Object[] arguments) { ... return invokeInternal(proxy, method, arguments); ... } private Object invokeInternal(Object proxy, Method method, Object[] arguments) { ... Method targetMethod = Statement.getMethod( target.getClass(), action, argTypes); ... return MethodUtil.invoke(targetMethod, target, newArgs); } ... } ... }
这里重点看下EventHandler.invokeInternal()
函数的代码逻辑,如注释:
private Object invokeInternal(Object var1, Method var2, Object[] var3) { //-------------------------------------part1---------------------------------- //作用:获取interface的name,即获得Comparable,检查name是否等于以下3个名称 String var4 = var2.getName(); if (var2.getDeclaringClass() == Object.class) { if (var4.equals("hashCode")) { return new Integer(System.identityHashCode(var1)); } if (var4.equals("equals")) { return var1 == var3[0] ? Boolean.TRUE : Boolean.FALSE; } if (var4.equals("toString")) { return var1.getClass().getName() + "@" + Integer.toHexString(var1.hashCode()); } } //-------------------------------------part2---------------------------------- //貌似获取了一个class和object if (this.listenerMethodName != null && !this.listenerMethodName.equals(var4)) { return null; } else { Class[] var5 = null; Object[] var6 = null; if (this.eventPropertyName == null) { var6 = new Object[0]; var5 = new Class[0]; } else { Object var7 = this.applyGetters(var3[0], this.getEventPropertyName()); var6 = new Object[]{var7}; var5 = new Class[]{var7 == null ? null : var7.getClass()}; } //------------------------------------------------------------------------------ try { int var12 = this.action.lastIndexOf(46); if (var12 != -1) { this.target = this.applyGetters(this.target, this.action.substring(0, var12)); this.action = this.action.substring(var12 + 1); } //--------------------------------------part3---------------------------------------- //var13获取了method的名称, var13=public java.lang.Process java.lang.ProcessBuilder.start() throws java.io.IOException Method var13 = Statement.getMethod(this.target.getClass(), this.action, var5); //-------------------------------------------------------------------------- //判断var13是否为空,当然不为空啦 if (var13 == null) { var13 = Statement.getMethod(this.target.getClass(), "set" + NameGenerator.capitalize(this.action), var5); } if (var13 == null) { String var9 = var5.length == 0 ? " with no arguments" : " with argument " + var5[0]; throw new RuntimeException("No method called " + this.action + " on " + this.target.getClass() + var9); } else { //-------------------------------------part4---------------------------------- //调用invoke,调用函数,执行命令 return MethodUtil.invoke(var13, this.target, var6); } //------------------------------------------------------------------------------ } catch (IllegalAccessException var10) { throw new RuntimeException(var10); } catch (InvocationTargetException var11) { Throwable var8 = var11.getTargetException(); throw var8 instanceof RuntimeException ? (RuntimeException)var8 : new RuntimeException(var8); } } }
有一说一看到这里的时候,就感觉 XStream 可能比较多的会通过动态代理作为 sink
DynamicProxyConverter 动态代理转换器
DynamicProxyConverter 即动态代理转换器,是 XStream 支持的一种转换器,其存在使得 XStream 能够把 XML 内容反序列化转换为动态代理类对象:
XStream 反序列化漏洞的 PoC 都是以DynamicProxyConverter
这个转换器为基础来编写的。
以官网给的例子为例:
com.foo.Blah com.foo.Woo blah
dynamic-proxy 标签在 XStream 反序列化之后会得到一个动态代理类对象,当访问了该对象的com.foo.Blah
或com.foo.Woo
这两个接口类中声明的方法时(即 interface 标签内指定的接口类),就会调用 handler 标签中的类方法com.foo.MyHandler
0x02 CVE-2013-7285
PoC
java.lang.Comparable Calc start
看到 PoC 这里大致是明白了,在之前有一段代码是读取每一个 XML 的节点,读取这些节点之后应该是用动态代理触发invoke()
了
触发代码
import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; import java.io.FileInputStream; // CVE_2013_7285 Exploit public class CVE_2013_7285 { public static void main(String[] args) throws Exception{ FileInputStream fileInputStream = new FileInputStream("G:\\OneDrive - yapuu\\Java安全学习\\JavaSecurityLearning\\JavaSecurity\\XStream\\XStream\\XStream-Basic\\src\\main\\java\\person.xml"); XStream xStream = new XStream(new DomDriver()); xStream.fromXML(fileInputStream); } }
漏洞原理
XStream 反序列化漏洞的存在是因为 XStream 支持一个名为DynamicProxyConverter
的转换器,该转换器可以将 XML 中dynamic-proxy
标签内容转换成动态代理类对象,而当程序调用了dynamic-proxy
标签内的interface
标签指向的接口类声明的方法时,就会通过动态代理机制代理访问dynamic-proxy
标签内handler
标签指定的类方法。
利用这个机制,攻击者可以构造恶意的XML内容,即dynamic-proxy
标签内的handler
标签指向如EventHandler
类这种可实现任意函数反射调用的恶意类、interface
标签指向目标程序必然会调用的接口类方法;最后当攻击者从外部输入该恶意 XML 内容后即可触发反序列化漏洞、达到任意代码执行的目的。
漏洞分析
下断点调试一下,这里前面的流程和分析 XStream 流程是类似的,会调用HierarchicalStreams.readClassType()
来获取到 PoC XML 中根标签的类类型
后面会跟进到mapper.realClass()
进行循环遍历,用来查找 XML 中的根标签为何类型(前面也都分析过了),接着是调用convertAnother()
函数对java.util.SortedSet
类型进行转换,我们跟进去该函数,其中调用mapper.defaultImplementationOf()
函数来寻找java.util.SortedSet
类型的默认实现类型进行替换,这里转换为了java.util.TreeSet
类型
接着就是寻找 Convert 的过程,这里寻找到对应的转换器是TreeMapConverter
转换器
往下调试,在AbstractReferenceUnmarshaller.convert()
函数中看到,会调用getCurrentReferenceKey()
来获取当前的 Reference 键,并且会将当前的 Reference 键压到栈中,这个 Reference 键后续会和保存的类型 ——java.util.TreeSet
类一一对应起来。
接着调用其父类即的FastStack.convert()
方法,跟进去,显示将类型压入栈,然后调用转换器 TreeSetConverter 的unmarshal()
方法:
在它第 61 行调用了treeMapConverter.unmarshalComparator()
方法,这个方法获取到了第二个 XML 节点元素,这个方法当时漏看了,这个方法还是比较重要的,它获取到了 xml 根元素的子元素。
跟进之后就变得一目了然了,其中判断reader
是否还有子元素
下面的reader.movedown()
方法做了获取子元素,并把子元素添加到当前 context 的 pathTracker
往下调试,在TreeSetConverter.unmarshal()
方法中调用了this.treeMapConverter.populateTreeMap()
,从这个方法开始,XStream 开始处理了 XML 里面其他的节点元素。跟进该函数,先判断是否是第一个元素,是的话就调用putCurrentEntryIntoMap()
函数,即将当前内容缓存到 Map 中:
跟进去,发现调用readItem()
方法读取标签内的内容并缓存到当前 Map 中
这里再跟进readItem()
方法,会发现比较有意思的一点是它又调用了HierarchicalStreams.readClassType()
和context.convertAnother()
方法,而这里的元素已经变成了第二个元素,也就是
,这里有点像是递归调用
可以跟进去看一下,这里通过查看mapper
可以知道目前拿去保存在 mapper 当中的还是两个元素,而 XStream 的处理,则会处理最新的一个(最里层的一个)
经过处理之后返回的 type 就为最新的一个子元素的类型,这里是com.thoughtworks.xstream.mapper.DynamicProxyMapper$DynamicProxy
,对应的转换器为DynamicProxyConverter
,跟进到其中来看具体处理。
先判断当前元素是否还有子元素,并获取该子元素进行后续判断
根据我们所编写的 xml,获取到的子元素为
,经过判断if (elementName.equals("interface"))
,如果为 true,则将目前
节点的元素获取到,再获得转换类型。
因为仍旧存在子元素,获取完
后重新进入这个迭代,下一个获取到的子元素是
。这里程序会判断是否等于 handler,如果等于 handler,则获取它标签所对应的类,并跳出迭代。
往下走,第 125 行调用了Proxy.newProxyInstance()
方法,这里是动态代理中的,实例化代理类的过程。第 127 行这里,调用context.convertAnother()
方法,跟进一下。对应的转换器是AbstractReflectionConverter
,它会先调用instantiateNewInstance()
方法实例化一个EventHandler
类
往下,跟进doUnmarshal()
方法,这里又是一层内部递归,从 xml 中可以看到
节点之下还有很多子节点(又看到了熟悉的hasChildren()
这时我们获取到的 type 为class java.lang.ProcessBuilder
,跟进unmarshallField()
方法
后面也都是类似的运行流程了,这里就不再废话,师傅们可以自行分析一下,是很容易看懂的;XSteam 虽然处理了 xml,且我们也基本明白了基础运行流程,但是最后漏洞触发这里还是要关注一下。
将所有的节点过完一遍之后,最终还是会走到treeMapConverter.populateTreeMap()
这个地方
跟进,直到第 122 行,调用put.All()
方法,里面的变量为 sortedMap,查看一下它的值可以发现这是一串链式存储的数据
最终是调用到EventHandler.invoke()
方法调用栈如下,还是比较简单的
invoke:428, EventHandler (java.beans)compareTo:-1, $Proxy0 (com.sun.proxy)compare:1294, TreeMap (java.util)put:538, TreeMap (java.util)putAll:281, AbstractMap (java.util)putAll:327, TreeMap (java.util)populateTreeMap:122, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
最后成功调用了java.lang.ProcessBuilder#start
方法,命令执行
0x03 漏洞修复
根据官方的修复手段,这里其实增加了黑名单
Users can register an own converter for dynamic proxies, thejava.beans.EventHandlertype or for thejava.lang.ProcessBuildertype, that also protects against an attack for this special case:
xstream.registerConverter(new Converter() { public boolean canConvert(Class type) { return type != null && (type == java.beans.EventHandler || type == java.lang.ProcessBuilder || Proxy.isProxy(type)); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { throw new ConversionException("Unsupported type due to security reasons."); } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { throw new ConversionException("Unsupported type due to security reasons."); }}, XStream.PRIORITY_LOW);
0x04 小结
XStream 最基础的漏洞是 CVE-2013-7285,通过这个漏洞可以很好的先认识 XStream 的基础运行流程,后续的漏洞挖掘和修复也算是一些《攻防史》,还是比较有意思的。
更多网安技能的在线实操练习,请点击这里>>
标签:
- Java 反序列化之 XStream 反序列化
- 怕俄罗斯断供液化天然气,德国“傍上了”美国|热点聚焦
- 唱好“双城记” 共建经济圈丨资阳:协同发展,文旅融合激活产业发展|天天日报
- 机构预计:6月中国50城新房成交面积同比跌逾两成|环球热头条
- 焦点资讯:俄国防部否认袭击俄雇佣军营地 消息和视频都是不真实的
- 全球快消息!淅川县一初中举办家庭教育与心理健康讲座
- 像相机一样变焦、填充画面细节,还能自定义风格,AI作画神器Midjourney又更新了 世界新视野
- 消息称 QQ 可使用微信账号登录-焦点消息
- 镀锌铁板的密度_镀锌铁板|焦点热文
- 让传统节日绽放时代新韵-环球头条
- 清洗饮水机小妙招白醋(清洗饮水机) 资讯
- 重点聚焦!火红“粽”韵让流动中国活力满满
- 全球今日报丨联通数科为电力建设领域注入数字化变革新动能
- 我国2023年电影端午档票房达9.09亿元
- 全球今热点:自然资源部:毫不动摇严守耕地红线 稳妥推进耕地进出平衡
- 早晨喝牛奶好还是睡前喝好?
- 上海本帮菜荤菜菜谱?
- 比亚迪DM-o来袭,全新混动系统,方程豹SF首发,颠覆越野车认知
- 环球即时:无锡国联3亿元超短债将于7月7日兑付 利率2.43%
- 每日观察!首次披露!杀害缉毒英雄蔡晓东的毒贩已被击毙
- 注意了!杭州网约车迎亚运出新政,7月17日起施行
- 快报:山东省纪委监委公开曝光4起基层治理不良现象及不担当不作为乱作为假作为典型问题
- 自然之道,健康之源——意大利健康品牌Optima Naturals
- 环球焦点!复旦中学与复旦大学签约,共建“数学教育基地”
- 三星开关机设置方法
- 聚焦钠离子电池技术创新与应用 乐普钠电完成1.5亿元A轮融资-每日视点
- 6月25日北海铁山港区营盘镇卫生院九价hpv疫苗预约
- 天天滚动:2023年河北高考声乐类一分一段表
- 盈通发布RTX 4060Ti显卡 售价3299元
- 全国首个设区市船员行业党委在泰州成立_焦点
- 微头条丨“吕振羽与马克思主义史学”学术研讨会在邵阳举行
- 毛体字查询_毛体字
- 坚定信心、不留退路!朱素芳检查推进安全生产大排查大整治和大气污染防治工作_头条焦点
- 【天天报资讯】6月25日常州清红硫酸价格暂稳
- 天天快看点丨课前三分钟讲什么好ppt(课前三分钟讲什么好)
- 办事处需要办营业执照吗现在_办事处需要办营业执照吗
- 黑旋风沂岭杀四虎概括200字(黑旋风沂岭杀四虎)
- 市场对需求担忧持续 国际油价6月23日下跌
- 醴陵农商银行举办党员干部学习二十大精神专题培训班
- 端午假期 兰州高星酒店预订量增长超19.5倍|天天新要闻
- 天天简讯:中蒙边境森林草原火灾联防工作会召开
- 流放之路搬砖一天能赚多少金币_流放之路搬砖一天能赚多少_今日热议
- 车主自爆差点闷死在特斯拉里:已经和解 全球即时
- 全球今亮点!乐享假期
- 17K小说网创始人刘英(笔名:血酬)去世-环球微速讯
- 千笔楼 | 这个端午,我们看到了什么?
- 当前关注:社校联动!宝山这个社区化身实践课堂
- 天天信息:世界上让人毛骨悚然的10个巧合,看完相信“冥冥之中自有天意”
- 全球速读:端午假期云南中缅、中越口岸迎出入境客流高峰
- 环球新消息丨端午期间,港股风电板块持续走低!
- 世界新动态:高考查分的心情你还记得吗?一起沉浸式体验
- 环球速递!韩国现代建设承揽沙特阿美50亿美元石化大单
- 世界今日报丨哈尔滨:旧小区新“蜕变” “改”出居民幸福感
- 海鲜干货品种大全_海鲜干货的做法
- 【当前独家】如龙维新极和如龙维新区别详情
- 翔楼新材(301160.SZ):南方精工并非公司客户|热门看点
- 全球今日讯!尽享运动乐趣,推荐几个适合夏日的运动方式→
- 世界热讯:公共 | 端午国潮文化节亮相北京顺义
- 《逆水寒》探索宗门药王谷 老兵服宗门探索药王谷完成攻略-热推荐
- 2023年聚酯纤维产能与市场供应平衡 热点评
- 神奇宝贝公司分享“1,008次遭遇”演示|环球实时
- 全球首个百万千瓦级“水光互补”电站在四川投产_热点评
- 全球新动态:淡斑祛斑效果排名第一,多方测评好用的几款!
- 全球百事通!智能耳机终端市场崛起 推动可穿戴设备需求增长 对行业提出新考验
- 世界实时:青海省2023高考分数线公布:本科一段文史类406,理工类330_当前观点
- 6月25日安徽星鑫化工氯化石蜡价格动态|热点
- 全球微速讯:债券基金当天买入是当天的净值吗 答案是这样的
- 最新预测!这一疾病,患者或达13亿人!
- 龙背上的骑兵3女主角背景介绍_龙背上的骑兵3 cg
- 全球已在寻找替代美元的货币 美国一手造成全球“去美元化”
- 华尔泰年产5万吨环己胺和二环己胺项目试生产
- 焦点消息!中听 | 以“沉淀文学”自嘲的年轻人,正在与现实和解
- 今年全国快递业务量已达600亿件 天天速看
- 快播:游戏行业集体逃避E3 2024/2025 游戏展 即将走向衰落
- 福州博爱中医院是男科吗?|天天报资讯
- 世界快看:《消失的她》夺2023年端午档冠军
- 世界热讯:自贡飞龙峡:绿水青山成就“金山银山”
- 什么专业可以报考二级建造师吗_哪些专业可以报考二级建造师考试
- 读者和主角绝壁是真爱全部的肉_读者和主角绝壁真爱肉的部分
- 93年属鸡男和96年属鼠女的婚姻配吗_93年属鸡的和什么属相最配最合适呢-世界实时
- 国际范儿,龙舟“划”向世界!端午节已成全人类共同文化遗产 环球观察
- 当前最新:智己 LS6 冲击月销万辆!比小鹏 G6 大一圈 预计售 23.98 万
- S站B站D站A站是什么 a站b站都是什么意思|观速讯
- 甘肃新能源装备制造业发展见闻 天天时快讯
- 春季想变美,这五种食物必须吃?
- 高温天用电用气用车都要“更用心”
- 山治和索隆都具备将星水平了吗?路飞一人独大的局面将被打破-全球今头条
- 靖宇推动基层行政执法提质增效
- 世界看热讯:京东新CEO许冉首次亮相,详解未来20年“35711”梦想
- 全长2712公里!沿着世界首条环沙漠铁路游新疆—— 世界今头条
- 瓦格纳撤军 乌克兰军方趁机反攻-天天报道
- 当前关注:视频 | 废旧钢铁回收再利用 来这里看一个超燃的“钢铁盛宴”
- 天天信息:沈铁迎来16年来最大幅度调图 释放客货运列车运力
- 广州考古发现珠三角面积最大的商时期文化遗存_报道
- 东北证券:收到中国证监会《行政处罚决定书》 |焦点讯息
- 理想L6打入30万以内,抢的将不仅仅是BBA市场,还有……
- 吃“知了猴”要注意什么?大量捕捉是否影响生态?快看专家解读
- 小i机器人美国公司正式启动
- 北京解除高温黄色预警! 全球即时
- 每日速读!弘扬体育精神 “乒”出精彩