0x00 简介
序列化的意义就在于方便存储和传输,永久的保存到硬盘中,通常保存在一个文件中。
序列化:将java对象转换为字节序列的过程
反序列化:序列化的逆过程,从储存区读出字节序列还原成对象的过程
0x01 漏洞成因
java应用在对用户的输入没有进行严格的检查时,即传入了不可信的数据做反序列化操作,那么攻击者就可以传入恶意构造的输入,在进行反序列化时达到攻击者预想的效果。
0x02 实现
Java中的API实现:
位置:Java.io.ObjectOutputStream java.io.ObjectlnputStream
序列化:ObjectOutputStream类-->writeObject()
注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中按Java的标准约定是给文件一个.ser扩展名
反序列化:ObjectInoutStream类-->readObject()
注:该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
00 Serializable接口
将要序列化的类实现Serializable接口,表明该类是可序列化(Serializable接口是一个标记接口,不用实现任何方法。)
package test1;
import java.io.Serializable;
public class user implements Serializable{
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
}
序列化
package test1;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class UserSerializable {
public static void main(String[] args) throws Exception {
user user = new user();
user.setName("biu");
//序列化对象
serialize(user);
}
public static void serialize(user user) throws Exception {
FileOutputStream fout = new FileOutputStream("user.ser");
ObjectOutputStream out = new ObjectOutputStream(fout);
//利用ObjectOutputStream类的函数writeObject对对象进行序列化
out.writeObject(user);
out.close();
fout.close();
System.out.println("序列化完成.");
}
}
序列化结果
反序列化
package test1;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
public class UserSerializable {
public static void main(String[] args) throws Exception {
user user = new user();
user.setName("biu");
//序列化对象
serialize(user);
//反序列化
user user1 = unserialize();
user1.setName("biubiu");
System.out.println(user1.getName());
}
//模拟序列化的自定义函数
public static void serialize(user user) throws Exception {
FileOutputStream fout = new FileOutputStream("user.ser");
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(user);
out.close();
fout.close();
System.out.println("序列化完成.");
}
//模拟反序列化的自定义函数
public static user unserialize() throws Exception{
//读取二进制文件
FileInputStream fileIn = new FileInputStream("user.ser");
//new一个ObjectInputStream对象
ObjectInputStream in = new ObjectInputStream(fileIn);
//利用ObjectInputStream类的readObject()方法对对象反序列化
user user = (user) in.readObject();
in.close();
fileIn.close();
System.out.println("反序列化完成.");
return user;
}
}
反序列化结果
将二进制文件反序列化为一个新的对象,重新对name进行赋值
01 Externalizable接口
实现Externalizable接口进行序列化和反序列化,必须实现writeExternal、readExternal方法,并且还要实现一个类的无参构造方法。
package test1;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class biu implements Externalizable {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
//实现无参构造方法
public biu(){
System.out.println(this.getClass()+"无参构造方法被调用");
}
//实现writeExternal方法
public void writeExternal(ObjectOutput out) throws IOException {}
//实现readExternal方法
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}
}
用法和Serializable接口一样,这里只展示结果
序列化结果
反序列化结果
将二进制文件反序列化为一个新的对象,重新对name进行赋值
0x03 readObject()方法
这个方法在反序列化漏洞中起到了重要的作用,因为在序列化过程中,JVM虚拟机会试图调用对象类里的 writeObject()和 readObject() 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject() 方法以及 ObjectInputStream 的 defaultReadObject() 方法。
实现了Serializable接口可以执行的方法包括readObject()、readObjectNoData()、readResolve(),以及实现了Externalizable接口的readExternal()方法。这些在找反序列化漏洞时都需要重点关注。
package test1;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class user implements Serializable{
public user(){
System.out.println(this.getClass()+"无参构造方法被调用");
}
public user(String name){
System.out.println(this.getClass()+"user(String name)构造方法被调用");
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
System.out.println(this.getClass() + "的toString()被调用");
return "userclass{name="+getName()+"}";
}
private void readObject(ObjectInputStream in) throws Exception{
//执行默认的readObject()方法
in.defaultReadObject();
System.out.println(this.getClass() + "的readObject()被调用");
//windows重点
Runtime.getRuntime().exec(new String[]{"cmd", "/c", name});
}
private String name;
}
在readObject()方法中存在Runtime.getRuntime().exec(new String[]{"cmd", "/c", name});name为执行命令参数,那么我们可以构造一个恶意的对象,将name赋值为我们想要执行的命令,那么当反序列化时就可以触发readObject()造成RCE。
00 序列化与反序列化
package test1;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
public class UserSerializable {
public static void main(String[] args) throws Exception {
user user = new user("calc");
user.setName("calc");
//序列化对象
serialize(user);
//反序列化
user user1 = unserialize();
System.out.println(user1);
}
public static void serialize(user user) throws Exception {
FileOutputStream fout = new FileOutputStream("user.ser");
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(user);
out.close();
fout.close();
System.out.println("序列化完成.");
}
public static user unserialize() throws Exception{
FileInputStream fileIn = new FileInputStream("user.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
user user = (user) in.readObject();
in.close();
fileIn.close();
System.out.println("反序列化完成.");
return user;
}
}
01 结果
class test1.useruser(String name)构造方法被调用
class test1.user的setName()被调用
序列化完成.
class test1.user的readObject()被调用
反序列化完成.
class test1.user的toString()被调用
userclass{name=calc}
可以看到在上图中不仅触发了重写的readObject()方法弹出calc程序,而且还触发了如toString()方法、setName()方法等,所以在实际寻找利用链时应该不局限于readObject()方法,其他方法中如果有执行命令的函数也可以进行利用。
当然,在实际情况中不可能会出现开发这样编写代码,这里是为了方便展示而进行编写,所以反序列化漏洞通常会需要Java的一些特性进行配合比如反射(invoke),然后就是利用链的寻找。
一般,java反序列化漏洞需要三个东西
1、反序列化入口
2、目标方法
3、利用链
0x04 补充知识点
00 下方的特征可以作为序列化的标志参考
数据以rO0AB
开头,基本可以确定这串就是JAVA序列化base64加密的数据。
如果以aced开头,那么他就是这一段java序列化的16进制。
01 利用工具
ysoserial是一个生成序列化payload数据的工具。
java -jar ysoserial-0.0.4-all.jar CommonsCollections1 '想要执行的命令' > payload.out
下载地址:发布 ·弗罗霍夫/伊索系列 (github.com)
参考
初探Java反序列化漏洞(一)_51CTO博客_java 反序列化文章来源:https://www.toymoban.com/news/detail-704319.html
清晰易懂java反序列化漏洞简介_1024zz的博客-CSDN博客_java反序列化漏洞文章来源地址https://www.toymoban.com/news/detail-704319.html
到了这里,关于Web安全--反序列化漏洞(java篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!