反序列化前提

1、被序列化和反序列化的类需要实现 Serializable 序列化 接口

2、并重写 readObject 方法,在重写的 readObject 方法中执行 objectInputStream.defaultReadObject 方法(否则无法读取序列化时属性的值)

3、具体原因这里不做解释,底层原理参考 https://www.cnblogs.com/zpchcbd/p/15126154.html 即可

 apache commons-collections(阿帕奇 公共集合)组件下曝出的一个反序列化链 简称 CC1 链

依赖环境

JDK <= 8u61

CC1 链如下图所示

参考:https://blog.csdn.net/WDWAGAAFGAGDADSA/article/details/122135422

1、首先分析 InvokerTransformer 类

1.1 InvokerTransformer 有一个 Transform 方法

1.2 该方法通过传入的 object 对象,通过参数名和参数类型去获取对应的方法

1.3 获取到方法对象后,再通过反射方法 inovke 中添加需要实现该方法的 对象 和方法所需 参数 去调用方法

1.4 iMethodName、IParamTypes、iArgs 可以通过构造方法去赋值,然后再调用 transform 方法即可(恶意代码执行成功)

因为序列化和反序列化操作不会对静态属性进行,所以需要找能替代 Runtime.getRuntime() 的类

Runtime.getRuntime() 这里实际是返回 static 属性的 Runtime 对象,所以不参与序列化,也就无法反序列化

2、分析 ConstantTransformer 类

2.1 ConstantTransformer 类有一个 transform 方法可以获对象,同时构造方法可以直接赋值该属性,且未做任何过滤

2.2 可以成功获取对象并使用

2.3 因为不能直接创建 static 属性去序列化,所以通过反射方式获取对象并执行方法

(门槛来了!这里你必须非常熟悉 java 语法,不然脑袋烧掉你都看不懂)

那么我们就要找到一个类,能帮我们去反射获并执行 Runtim.getRuntime().exec() 方法,还要能传入 calc 值

3、分析 chainedTransformer 类(很巧,该类帮我们实现了该功能)

3.1 这个就帮我们完成一个循环调用,每次循环出来 object 对象都会作为参数进入下一次循环(就满足 2.3 循环条件)

3.2 chainedTransformer 类的 iTransformers 属性可以通过构造方法赋值

3.3 chainedTransformer 的 ChainedTransformer 方法从数组中取出每一个对象去调用 transform 方法

前面分析了 InvokerTransformer 类和 ConstantTransformer 类,通过通过 transform 方法去实现攻击链

尝试编写测试类

3.4 通过构造方法,已赋值好属性,debug 进入 transform 方法

3.5 第1次循环,这里第一个参数是 ConstantTransformer 类,并且通过 transform 返回 iConstant 属性,就是返回 Runtime.class Object 对象,并循环作为下次的参数

3.6 第2次循环 对象 InvokerTransformer 的 Transform 方法,就需要 Runtime 作为参数

3.7 第3次循环

3.8 第4次循环(这里就参考 1.4 即可,实际上就是放入 Runtime.getRuntime() 作为参数)

3.9 进入 debug

3.10 跳出方法,执行成功

那么问题来了,需要找到一个可以触发 chainedTransformer 类的 Transform 方法的类

4 分析 TransformedMap 类(正好此 TransformedMap 类中的类 Entry 的 setValue 方法中触发 checkSetValue,而此方法触发 Transform 方法)

4.1 TransformedMap.decorate() 方法是 TransformedMap 类的创建对象方法,这里参数 map 传入,是触发 setValue 方法的前提必须有值

4.2 首先通过 entrySet 方法获取 TransformedMap.Entry 对象,然后调用 setValue 跟进(前提是需要传入map)

4.3 跟进 checkSetValue 方法

4.4 发现 valueTranformer 属性调用 transform 方法(传入的参数不影响正常执行)

4.5 此 valueTranformer 可通过 TransformedMap 类的 decorate 方法赋值

4.6 所以可以通过在构造方法传入 transformedChain 去间接调用 transform 方法

4.7 执行成功

现在就找谁会调用 transformedMap.entrySet() 遍历对象的 setValue 方法,只有此方法以下才会触发 transform 方法

5、 分析 Annotation Invocation Handler 方法

5.1 该类的 readObject 方法中实现了 setValue 方法,正好与上面 entrySet 取值步骤对应,其中 memberValues 属性可以通过构造方法设置

5.2 编写最终恶意类链

5.3 获取构造方法并填写入形参,并设置构造方法访问权限,因为构造方法是访问修饰符默认没写的,所以需要设置构造方法的访问权限,否则无法使用,通过 newInstance 方法以构造方法创建对象并写入实参,最后交给封装方法完成反序列化

5.4、传入 Target 类是因为参数必须继承 Annotation

5.5 这里使用 Target 类,使用@interface自定义注解时,自动继承了 java.lang.annotation.Annotation 接口

5.6 进入到 readObject,需要绕过这两个判断才调用 setValue 方法(重点又来了)

5.7 这里 type 实际就是 Target,拆分返回类型和方法名

5.8 var3.get(var6) 非常关键,实际指向 var3 -> var2 -> Target,实际指向 var6 -> var5 -> var4 -> map(这里我们之前构造函数传入的map)

5.9 var3.get("value") 实际获取的是在 Target 类中名叫 value 方法名的返回类型 ElementType 类给 var7

5.10 第1个判断,var7 = ElementType 绕过一个判断,var5.getValue() 获取到的是 map 中"value"参数 赋值给 var8

5.11 第2个判断(1),var7(ElementTpye)没有实现 var8(String)该接口或实现类,返回 false,因 ! 所以返回 true

5.11 第2个判断(2), var8(String)不是 ExceptionProxy 类或子类所创建的对象,返回 false,因 ! 所以返回 true

5.12 绕过两个判断执行成功

备注:在 JDK 8u71 开始,就修改了 Annotation Invocation Handler 类的源码,导致该利用链不可用。

参考文章:

@interface使用详解

 

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/shaoqiblog/p/17245787.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!