'Java反序列化-cc1-TransformedMap'
1、Apache Commons Collections包和简介
Apache Commons是Apache开源的Java通用类项目在Java中项目中被广泛的使用,Apache Commons当中有一个组件叫做Apache Commons Collections,主要封装了Java的Collection(集合)相关类对象。
以下是Collections的包结构和简单介绍,如果你想了解更多的各个包下的接口和实现,请参考Apache Commons Collections 3.2.2 API文档 。
- 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包中都是集合类,并且可以接收任意对象作为参数。
2、环境配置
使用jdk:1.8u65
8u71之后已修复不可利用
安装maven
https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip
配个环境变量
maven -v 查看maven版本
idea设置
创建个maven项目。
使用idea->create new project->maven。
选中 Project Structure,修改 Modules
选择setting 设置 build --> complier --> java complier 把自己的项目设置version为8。
https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1
复制cc 3.2.1版本的配置,到pom.xml中
1 | <dependencies> |
配置idea maven build
配置maven settings.xml
1 | //镜像部分 |
更新dependencies(reimport)
下载源文件
测试加载类,可正常加载
import org.apache.commons.collections.functors.InvokerTransformer;
我们还要做一件事,修改 sun 包。(java安全-环境,这篇文章中已经说过)
因为我们打开源码,很多地方的文件是 .class 文件,是已经编译完了的文件,都是反编译代码,我们很难读懂,所以需要把它转换为 .java 文件。
解压jdk1.8.0_65中的src.zip
将sun放到src中即可
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
src/share/classes/sun
3、攻击链
(1)、寻找尾部危险方法(反射/动态加载字节码)
作者是发现了在org.apache.commons.collections.functors包中的Transformer类。
Transformer类中的Transformer的实现类
快捷键 crtl alt B
InvokerTransformer类中的transform
1 | public Object transform(Object input) { |
写的非常像后门,getClass加载类,然后getMethod获取方法,invoke反射调用。
演示一个标准的通过反射调用runtime的方法:
1 | Runtime runtime = Runtime.getRuntime(); |
写成InvokerTransformer类的transform写法。
1 | Runtime runtime = Runtime.getRuntime(); |
现在我们找到了调用危险方法的地方,所以准备反推
(2)、初步寻找链子
危险方法(反射/动态加载字节码)(cc1-TransformedMap这里用的是反射) --> InvokerTransformer.transform --> O2.transform(O.aaa) --> A.readObject(O.aaa)
Ctrl+Shift+Alt+F7
设置usages,project and libraries。
例如cc包的map包中LazyMap类调用了transform方法,这时思路如下:
InvokerTransformer.transform -->LazyMap.transform(O.get) --> A.readObject(O.get)
最终找到
TransformedMap类中存在checkSetValue()方法调用了transform()方法
接下来我们去看一看valueTransformer.checkSetValue的valueTransformer
跟进方法
因为TransformedMap的构造方法作用域是protected,我们还需要去找一找谁调用了TransformedMap的构造方法。
在decorate()静态方法中创建了TransformedMap对象
到这一步,尝试将其作为链子的开头,编写 POC
1 | Runtime runtime = Runtime.getRuntime(); |
解释流程:
先实例化个runtime,实例化InvokerTransformer加载执行命令,然后下一步肯定是调用.transform。
创建个hashmap,利用TransformedMap.decorate添加对象,加载exec方法
实例化TransformedMap对象,然后调用checkSetValue方法,修改权限,利用invoke加载runtime执行命令
(3)、完整链子
目前找到的链子位于checkSetValue当中,去找.decorate的链子,发现无法进一步前进了,所以我们回到checkSetValue重新找链子。
继续find usages,找到了parent.checkSetValue(value);调用了checkSetValue
并且,此类是TransformedMap的父类
调用checkSetValue方法的类是AbstractInputCheckedMapDecorator类中的一个内部类MapEntry
setValue()**实际上就是在 Map 中对一组 entry(键值对)**进行setValue()操作。
所以,我们在进行.decorate方法调用,进行 Map 遍历的时候,就会走到setValue()当中,而setValue()就会调用checkSetValue
跟进Abstractinputcheckedmapdecorator setValue,到继承类AbstractMapEntryDecorator setValue
再到Map 的 setValue
所以,我们在进行.decorate方法调用,进行 Map 遍历的时候,就会走到setValue()当中,而setValue()就会调用checkSetValue
演示代码如下:
1 | Runtime runtime = Runtime.getRuntime(); |
总结:遍历一个Map最终执行setValue()方法
(4)、找readObject
之前链子是到setValue的,所以我们在setValue处,find usages
查询所有地方
找到AnnotationInvocationHandler类,InvocationHandler这个后缀,我在基础篇-动态代理里面提到过,是用做动态代理中间处理,因为它继承了InvocationHandler接口。
要调用setValue()方法,我们需要完成几个条件。
1 | for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { |
遍历map功能
1、成员变量memberType不为null,成员变量存在
2、memberType类型
3、最终调用memberValue.setValue
看一下AnnotationInvocationHandler传参
写poc
此时理想情况下的poc
1 | Runtime runtime = Runtime.getRuntime(); |
通过反射方式实例化AnnotationInvocationHandler,设置权限,加载构造类,加载 注解类,decorateTransformedmap。
decorateTransformedmap去调用hashmap和exec执行命令。
问题
1、Runtime对象不可序列化,需要通过反射将其变成可以序列化的形式。
可以看到下图:runtime没有继承Serializable类
2、解决setValue的两个if判断
3、setValue()的传参,是需要传Runtime对象的;而在实际情况当中的setValue()的传参是这个东西,如下图:
解决问题1
runtime对象虽然不能进行序列化,但是class是可以序列化的。查看Class代码如下:
1 | public final class Class<T> implements java.io.Serializable, |
调用runtime class类
利用反射获取getRuntime方法,再获取exec方法,然后反射调用exec
所以代码如下:
1 | Class cclas = Runtime.class; |
我们将这个反射的Runtime改造为使用InvokerTransformer调用的方式。
1 | Method getRuntimeMethod = (Method)new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class); |
可以看到他是transform的循环利用,即前一个值作为后一个的参数。
所以想到了ChainedTransformer类,作用如下:
列表中的所有元素都要实现 Transformer 接口,同时在transform方法中会对Transformer数组中的元素按照顺序调用transform方法,同时将上一个元素的返回对象作为输入传递给下一个元素的transform方法中
代码如下:
1 | public ChainedTransformer(Transformer[] transformers) { |
修改后的代码
1 | Transformer[] transformers = new Transformer[]{ |
再把它与decorate的链子结合一下
解决问题2 进入到 setValue 方法
断点追踪,name为key,搜索为null
无法进入Object value = memberValue.getValue();
直接跳出方法。
序列化时,跳入AnnotationInvocationHandler readObject方法,
annotationType = AnnotationType.getInstance(type);
type为我们传入的override
annotationType.memberTypes();
获取成员变量
memberTypes.get(name);
获取成员变量的名字
hashmap.put(“key”, “value”);
也就是说在 override中找key。
但是override中没有key,所以为null
解决此问题:
找有成员方法的class。
比如Target
此时符合第一个if了
能够进入到memberValue.setValue
追踪setValue
step into
1 | public Object setValue(Object value) { |
这里的valueTransformer.transform(value)
继续追踪,发现其实也就相当于
ChainedTransformer.transform
发现value为AnnotationTypeMismatchExceptionProxy
解决问题3 载入runtime类
此时我们想到ConstantTransformer方法
作用:调用transform方法时会直接返回构造函数中传入的对象
1 | public ConstantTransformer(Object constantToReturn) { |
所以直接利用此方法,调用runtime的class
new ConstantTransformer(Runtime.class)
最终poc
1 | Transformer[] transformers = new Transformer[]{ |
总体利用链
1 | AnnotationInvocationHandler readObject() --> memberValue.setValue() --> checkSetValue |
可能遇到的报错
1、Error:java: Compilation failed: internal java compiler error
解决方案
Target bytecode version改为8
2、老问题:找不到sun包
find usages,找setValue的时候,找不到
sun\reflect\annotation AnnotationInvocationHandler.java
File -> Project Structure --> SDKs中对应jdk --> Sourcepath下添加我们openjdk的zip包(或者加了sun包的原始jdk src)
重新加载src文件夹,或者直接导入openjdk的zip
3、idea自动导包
报错: 不兼容的类型: org.apache.commons.collections.functors.InvokerTransformer无法转换为javax.xml.transform.Transformer
解决方法:
删除自动导入的包
import javax.xml.transform.Transformer;
手动导入如下包:
import org.apache.commons.collections.Transformer;