文章目录
- 1 你必须知道的点
- 2 CC1链的环境准备
- 3 CC1链分析
- 4 动态调试
- 5 总结
- 6 参考链接
1 你必须知道的点
1.1 反序列化利用链的起点是readObject()方法
Java的序列化机制允许将对象的状态保存到一个字节流中,之后可以从这个字节流中恢复(或“反序列化”)出对象。这个过程中,ObjectInputStream
类负责读取这些字节流,并尝试根据包含的类型信息重新创建对象。为了支持复杂对象和自定义类型,Java提供了自定义反序列化过程的能力,这通常通过重写readObject()
方法来实现。
也就是说我们可以通过在将要被序列化或反序列化的类中定义readObject方法,来实现自定义的反序列化操作,当然前提是,被序列化的类必须有此方法,并且方法的修饰符必须是private。
序列化则自定义writeObject方法。
当ObjectInputStream
读取到一个可序列化的对象时,如果该类定义了readObject()
方法,则会调用该方法来完成反序列化过程。因此,如果readObject()
方法中存在不安全或未经验证的代码(如直接反序列化用户控制的数据),那么就会执行内部的代码,那么它就可能成为反序列化利用链的起点。
一句话总结就是:如果一个类自定义了readObject()
方法,则该类在反序列化时就会调用该方法完成反序列化过程,如果该方法中存在“恶意代码”,也会执行。所以,自定义了readObject()
方法的类就是我们寻找的入口点。
反序列化利用链形象比喻一下~
- eadObject为反序列化入口点。
- 些方法存在命令执行的可能性。
- 反序列化利用链就是需要将readObject及存在命令执行的方法联系在一起。
- 就像走迷宫,readObject为入口,存在命令执行的方法为出口,从入口到出口就形成了一条链(利用链)。
- 我们在找链的时候,就可以从两个方面找,从入口或者出口找,或者两头并进。
1.2 回顾反射执行系统命令
直接执行命令:
String[] command = {"open","-a","/System/Applications/Calculator.app/Contents/MacOS/Calculator"};
Runtime.getRuntime().exec(command);
反射执行命令:
Class clazz = Runtime.class;
Method getRuntime = clazz.getDeclaredMethod("getRuntime", null);
Runtime runtime = (Runtime)getRuntime.invoke(null, null);
runtime.exec("open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator");
1.3 相关类の功能简单介绍
提前了解下,后面分析时不晕!!
1.3.1 InvokerTransformer类
InvokerTransformer
类在实例化时,赋值iMethodName、iParamTypes和iArgs。- 使用
InvokerTransformer
对象调用transform()
方法,会以反射的方式执行任意方法。
举个例子:
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class} ,
new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Runtime runtime = Runtime.getRuntime();
invokerTransformer.transform(runtime);
上述内容,相当于执行了:
Runtime runtime = Runtime.getRuntime();
// invokerTransformer.transform()内部执行了
Class cls = runtime.getClass();
Method method = cls.getMethod("exec", new Class[]{String.class});
return method.invoke(runtime, new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"});
会通过反射方式执行弹出计算器操作。
1.3.2 ChainedTransformer类
ChainedTransformer
类在实例化时,会传入Transformer[]
数组,并将值transformers
赋值给iTransformers
。- 使用
ChainedTransformer
对象调用transform()
方法,会挨个执行transformers
数组元素transformer
的transform()
方法,其参数为传入的object对象。 - 也就是说,单独执行
transformer.transform()
和将transformer
放入数组中执行ChainedTransformer.transform()
效果是一样的。 - 还有一个非常重要的点:每次循环获得的
object
会当做参数传给下一个transform()
,也就是数组中的transformer
是有前后联系的,不是单独的。
1.3.3 ConstantTransformer类
ConstantTransformer
类在实例化时,会传入一个对象赋值给iConstant
属性。- 当
ConstantTransformer
对象调用transform()
方法时,不管传入什么,都将返回一个固定的值,即实例化时传入的对象,即iConstant
属性值。
1.3.4 总结一下上述3个类调用transform()
方法的不同
上述3个类都实现了Transformer
接口,并都重写了transform()
方法,因此在调用transform()
方法时,会有不同的执行结果。
并且上述3个类都实现了Serializable
接口,可被序列化和反序列化。
InvokerTransformer.transform()
- 以反射方式执行任意方法
ChainedTransformer.transform()
- 遍历链内部的所有
Transformer
执行transform()
- 前一个执行
transform()
的结果当做后一个transform()
方法的参数传入
ConstantTransformer.transform()
- 返回固定值,值为构造方法传入的参数值
2 CC1链的环境准备
2.1 版本问题
CC1链主要利用的是Apache Commons Collections 3.2.1
版本中的一个反序列化漏洞。
CC1链在JDK 8u71及之后的版本中被修复,因此需要使用JDK 8u71之前的版本
。
JDK下载地址:https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
这里选择了JDK 8u66
。
2.2 搭建项目
1)创建一个mevan项目,JDK选择8u66
2)pom.xml添加commons-collections 3.2.1
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
3)准备JDK源码,方便调试
下载地址:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
下载解压之后将文件中的sun目录(具体路径为jdk-af660750b2f4\jdk-af660750b2f4\src\share\classes
)放入JDK 8u66的家目录下。
然后在项目中的SDKs中配置sun目录为Classpath,如图。
3 CC1链分析
3.1 InvokerTransformer#transform方法
先定位到InvokerTransformer#transform方法。
为什么要定位到这里?因为这个方法是CC1的执行恶意命令的位置。
前辈们怎么发现的这里?(猜测:)用的多了就发现了…(这里就是前面所说的迷宫的出口)。
可以发现transform
方法,接收一个Object参数,然后对该Object通过反射获取其类对象
,然后获取其方法对象
,之后invoke
调用该方法。涉及的核心代码为59-61行,如下:
这里明显就是通过反射调用传递进来的对象的某个方法来执行~
这里面涉及到几个参数
this.iMethodName
:反射使用的方法名(方法名)this.iParamTypes
:反射使用的方法的参数类型列表(参数类型)this.iArgs
:被调用方法的参数列表(参数值)
这几个参数都是this.开头的,所以是由该类InvokerTransformer
的构造函数赋予其值的。
找一下构造函数:
有了这些,我们就可以简单写一下利用代码:(来打开本地计算器)
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"}
);
Runtime runtime = Runtime.getRuntime();
invokerTransformer.transform(runtime);
这里是可以执行命令的,但是这样还不行,因为我们最终需要找到readObject()
方法处,也就是反序列时执行readObject()
方法,进而自动执行transform()
方法才行。
也就是:后面的代码不能使用
invokerTransformer.transform(runtime);
方式调用transform
了。
接下来需要找一下哪个位置有调用transform()
方法:选中方法,右击-“Find Usages”可查看方法的调用情况
标签:InvokerTransformer,调用,全网,CC1,transform,new,方法,class,最菜 From: https://blog.csdn.net/qq_45305211/article/details/141720808