<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>

    fastjson1224的TemplatesImpl鏈反序列化分析

    VSole2022-01-07 14:24:04

    STATEMENT

    聲明

    由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,雷神眾測及文章作者不為此承擔任何責任。

    雷神眾測擁有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經雷神眾測允許,不得任意修改或者增減此文章內容,不得以任何方式將其用于商業目的。

    fastjson的序列化與反序列化

    首先,分析fastjson的反序列化前我們先來了解一下fastjson,我們新建一個User類:

    public class User {public Long id;public String name;public Long getId() {System.out.println("getid="+this.id);return id;}public void setId(Long id) {this.id = id;System.out.println("setid="+this.id);}public String getName() {System.out.println("getname="+this.name);return name;}public void setName(String name) {System.out.println("setname="+this.name);this.name = name;}}
    

    fastjson提供了三種反序列化的方法,分別是:

    String jsonString = "{\"id\":2,\"name\":\"guest\"}\n";// 反序列化代碼示例1Object ob1 = JSON.parse(jsonString);System.out.println("ob1="+ob1);// 反序列化代碼示例2Object ob2 = JSON.parseObject(jsonString);System.out.println("ob2="+ob2);// 反序列化代碼示例3Object ob3 = JSON.parseObject(jsonString,User.class);System.out.println("ob3="+ob3);
    

    那么這三個方法有什么區別呢,接下來,我們在代碼中set一下參數的值,運行一遍看看:

    public class main {public static void main(String[] args) {User guestUser = new User();guestUser.setId(2L);guestUser.setName("guest");// fastjson提供toJsonString接口實現序列化// fastjson提供parseObject來分別實現反序列化// 序列化示例String jsonString = JSON.toJSONString(guestUser);System.out.println("序列化數據="+jsonString);...}}
    

    運行后發現ob1和ob2返回的是JSONObject,而ob3則是實際的類對象,并且ob3調用了User類中全部set方法,ob1與ob2則是set、get方法均未調用,那么接下來我們就來看看他們在調用set與get方法的區別

    我們新寫一段代碼,首先傳入User類的一句序列化后的字符串,而后使用三種方法來反序列化它,看看分別調用了什么方法

    String parseString = "{\"@type\":\"User\",\"id\":2,\"name\":\"guest\"}";System.out.println("parse方法的調用結果");Object ob11 = JSON.parse(parseString);System.out.println("ob11="+ob11);System.out.println("----------------------");System.out.println("parseObject方法的調用結果");Object ob12 = JSON.parseObject(parseString);System.out.println("ob12="+ob12);System.out.println("----------------------");System.out.println("parseObject(xx,class)方法的調用結果");Object ob13 = JSON.parseObject(parseString,User.class);System.out.println("ob13="+ob13);
    

    結果:

    我們可以發現,parse和parseObject都返回了全部的set方法,并且parseObject返回了全部的get方法,parseObject(xx,class)返回了全部的set方法,那么我們是否可以得出結論,全部的方法都可以調用set方法呢?那么接下來我們就來看看調用到的set、get方法分別存在什么限制,這其實就是fastjson的特性

    fastjson特性

    首先,我們來看set與get方法的限制:

    // com/alibaba/fastjson/util/JavaBeanInfo.javapublic class JavaBeanInfo{public static JavaBeanInfo build(Class clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {/*** 判斷set方法函數*/for (Method method : methods) { //int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;String methodName = method.getName();// 長度比4大if (methodName.length() < 4) {continue;}// 非靜態方法if (Modifier.isStatic(method.getModifiers())) {continue;}// 返回類型不能是void或者當前類// support builder setif (!(method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(method.getDeclaringClass()))) {continue;}Class[] types = method.getParameterTypes();// 不能有傳入的參數if (types.length != 1) {continue;}// annotation = null,跳過JSONField annotation = method.getAnnotation(JSONField.class);if (annotation == null) {annotation = TypeUtils.getSuperMethodAnnotation(clazz, method);}if (annotation != null) {// ......略過}// 以set為開頭if (!methodName.startsWith("set")) {continue;}char c3 = methodName.charAt(3);String propertyName;// 第4個字母大寫if (Character.isUpperCase(c3) //|| c3 > 512 // for unicode method name) {if (TypeUtils.compatibleWithJavaBean) {propertyName = TypeUtils.decapitalize(methodName.substring(3));} else {propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);}} else if (c3 == '_') {propertyName = methodName.substring(4);} else if (c3 == 'f') {propertyName = methodName.substring(3);} else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {propertyName = TypeUtils.decapitalize(methodName.substring(3));} else {continue;}Field field = TypeUtils.getField(clazz, propertyName, declaredFields);if (field == null && types[0] == boolean.class) {String isFieldName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);field = TypeUtils.getField(clazz, isFieldName, declaredFields);}// filed = null,跳過JSONField fieldAnnotation = null;if (field != null) {// ......}// propertyNamingStrategy = null,跳過if (propertyNamingStrategy != null) {// ......}add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures,annotation, fieldAnnotation, null));}/*** 判斷get方法函數體:*/for (Method method : clazz.getMethods()) { // getter methodsString methodName = method.getName();// 長度不能小于4if (methodName.length() < 4) {continue;}// 不能是靜態方法if (Modifier.isStatic(method.getModifiers())) {continue;}// 以get開頭并且第4位是個大寫字母if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) {// 不能有傳入的參數if (method.getParameterTypes().length != 0) {continue;}// 返回值繼承于Collection Map AtomicBoolean AtomicInteger AtomicLong之一if (Collection.class.isAssignableFrom(method.getReturnType()) //|| Map.class.isAssignableFrom(method.getReturnType()) //|| AtomicBoolean.class == method.getReturnType() //|| AtomicInteger.class == method.getReturnType() //|| AtomicLong.class == method.getReturnType() //) {// .........// 對get方法進行處理的函數,略過}
    

    所以,根據以上代碼,我們可以總結出調用到set、get方法的限制:

    • set方法條件
    1. 方法名長度大于4且以set開頭,且第四個字母要是大寫
    2. 非靜態方法
    3. 返回類型為void或當前類
    4. 參數個數為1個
    • get方法條件
    1. 方法名長度大于等于4,且以get開頭且第4個字母為大寫
    2. 非靜態方法
    3. 無傳入參數
    4. 返回值類型繼承自Collection Map AtomicBoolean AtomicInteger AtomicLong

    并且,parseobject()這個方法中多了一步toJSON操作,會調用全部的get方法:

    這樣,上面調用到的set、get方法就全部說通了。

    下面我們看看fastjson的另一種特性,就是@type這個參數,開發者本意是序列化時使用SerializerFeature.WriteClassName會寫入類型信息,從而使反序列化時不會丟失類型信息,并且反序列化時自動進行類型識別,但是這就造成了一個問題,type參數可以指定反序列化任意的類,然后去調用set、get方法,我們來看看實際的代碼是如何運作的

    在序列化是寫入類型信息,此時的序列化后的字符串就有@type參數:

    我們在看看輸入@type參數,他的反序列化結果:

    看似這種情況很正常,反序列化使用到set、get方法恢復數據,但是在可以調用任意類的情況下,這就很恐怖了,如果set或者get方法中有可利用的點的情況下,就會造成RCE。

    jdk7u21TemplatesImpl鏈

    說到fastjson的TemplatesImpl鏈,就不得不提jdk7u21的TemplatesImpl鏈,fastjson的這種利用方法其實和jdk7u21的TemplatesImpl鏈很像,jdk7u21由以下代碼出發RCE

    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    public class jdk7u21_nekoc {public static void main(String[] args) throws Exception {TemplatesImpl calc = (TemplatesImpl) Gadgets.createTemplatesImpl("/System/Applications/Calculator.app/Contents/MacOS/Calculator");calc.getOutputProperties();}}
    

    gadegets類是在ysoserial里拔出來的,跟進觸發漏洞的calc.getOutputProperties();方法,發現最終由obj.newinstance觸發惡意代碼,由于newinstance示例化會默認觸發static及構造方法,所以payload可以寫在這兩個中的其一,并且,這里注意,getOutputProperties方法正好符合fastjson中對get方法名字的限制。

    我們這里不再具體分析jdk7u21,我們來跟進看一下該鏈子的一些限制條件,跟進代碼:

    return newTransformer().getOutputProperties();

    首先,我們發現:

    com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java中,存在兩個限制條件:

    public class TemplatesImpl {private Translet getTransletInstance()throws TransformerConfigurationException {try {// 第一個條件 _name不為nullif (_name == null) return null;
    // 第二個條件 _class不為null,進到defineTransletClasses里if (_class == null) defineTransletClasses();// ......}}
    

    第一個條件就是_name不為null,第二個條件是_class不為null,這樣才能進到defineTransletClasses方法,我們繼續來看看defineTransletClasses方法里邊的判斷:

    private void defineTransletClasses()throws TransformerConfigurationException {
    // 第三個條件:_bytecodes不為nullif (_bytecodes == null) {ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);throw new TransformerConfigurationException(err.toString());}
    // 7u21TransletClassLoader loader = (TransletClassLoader)AccessController.doPrivileged(new PrivilegedAction() {public Object run() {// 這里應該是第四個條件,這里7u21和高版本的7u80有區別return new TransletClassLoader(ObjectFactory.findClassLoader());}});// 7u80TransletClassLoader loader = (TransletClassLoader)AccessController.doPrivileged(new PrivilegedAction() {public Object run() {// 這里就是第四個限制條件,_tfactory參數存在一個getExternalExtensionsMap方法return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());}});
    try {final int classCount = _bytecodes.length;_class = new Class[classCount];
    if (classCount > 1) {_auxClasses = new Hashtable();}
    for (int i = 0; i < classCount; i++) {_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();
    // Check if this is the main class// 第五個限制條件:_bytecodes必須是ABSTRACT_TRANSLET子類if (superClass.getName().equals(ABSTRACT_TRANSLET)) {_transletIndex = i;}else {_auxClasses.put(_class[i].getName(), _class[i]);}}
    if (_transletIndex < 0) {ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);throw new TransformerConfigurationException(err.toString());}}catch(Exception e){//...}}}
    

    我們跟進代碼首先會發現第三個限制條件,即_bytecodes不為null,繼續向下看,這里的代碼版本不同,內容就不一樣,在7u21中,不存在有關_tfactory的限制,在7u80中,就存在該限制,這里要求_tfactory參數存在一個getExternalExtensionsMap方法,為了版本通用,所以在poc里加上對tfactory的限制條件,繼續向下看,我們可以看到第五個條件,即_bytecodes必須是ABSTRACT_TRANSLET子類,并且,payload要寫在_bytecode對應的類的靜態方法或者構造方法里,這樣,我們就集齊了全部的限制條件,小結一下就是:

    1. name != null
    2. _class == null
    3. _bytecodes != null
    4. _tfactory需要有一個getExternalExtensionsMap方法
    5. _bytecodes的類必須是ABSTRACT_TRANSLET的子類
    6. payload要在_bytecode對應的類的靜態方法或者構造方法里

    1224 TemplatesImpl鏈

    jdk7u21在fastjson1224的應用還需要一點點條件,用parseObject()時,必須用JSON.parseObject(jsonString, Feature.SupportNonPublicField);這樣的格式,必須要有Feature.SupportNonPublicField參數才可以,payload中有部分參數是private屬性,需要在這里設置一下才能被接受;使用parse()時,需要JSON.parse(jsonString,Feature.SupportNonPublicField); 這樣的格式。

    在編寫payload時,需要用到javasist類,這里不再描述該類。

    最終的部分payload:

    看到最后的代碼可能會有些疑問,這里的_tfactory為什么是空而不是一個有getExternalExtensionsMap方法的類對象?_OutputProperties是怎么調用到getOutputProperties方法的?

    我們來看看這兩個問題的解答。

    首先是第一個,_tfactory為什么是空而不是一個有getExternalExtensionsMap方法的類對象呢,我們跟一下代碼,不難發現,解析這幾個參數的時候,如果發現參數值為空對象,就會新建一個該參數應有的格式的對象實例,將其賦值給該參數

    那么,_tfactory應有的格式為啥是它呢?咱們看一下定義就知道了

    所以,_tfactory其實本身就是TransformerFactoryImpl類的對象,賦值為空就系那個回家了一樣,完全是可以的。

    接下來我們看看第二個問題,_OutputProperties是怎么調用到getOutputProperties方法的呢,我們跟進代碼就可以看到,在處理參數的時候,代碼會將_OutputProperties前面的下劃線替換為空,此時反序列化時就回去調用他的get方法,即getOutputProperties,所以其實poc生成中得_OutputProperties有沒有下劃線都是可以的,這樣我們就在fastjson1224版本中完成了TemplatesImpl鏈的分析。

    序列化fastjson
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    STATEMENT聲明由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,雷神眾測及文章作者不為此承擔任何責任。雷神眾測擁有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經雷神眾測允許,不得任意修改或者增減此文章內容,不得以任何方式將其用于商業目的。
    HW藍隊初級面試總結
    2022-10-12 07:00:02
    一、sql注入原理、分類、繞過原理:產生sql注入漏洞主要因為沒有對接受到的參數進行過濾、驗證和處理直接拼接到了sql語句中,然后直接執行該sql語句,這樣就會導致惡意用戶傳入一些精心構造的sql代碼,與后臺sql語句拼接后形成完整的sql語句執行,達到攻擊者的目的。
    Fastjson Develop Team發布安全公告,修復了一個存在于Fastjson1.2.80 及之前版本中的反序列化漏洞。漏洞編號:暫無,漏洞威脅等級:高危。
    本篇文章是Fastjson框架漏洞復現,記錄了近幾年來爆出的Fastjson框架漏洞,主要分為四個部分:Fastjson簡介、Fastjson環境搭建、Fastjson漏洞復現、Fastjson工具介紹。 本篇文章由淺入深地介紹了Fastjson的一系列反序列化漏洞,基于RMI或LDAP方式反序列化漏洞利用對Fastjson進行RCE。在學習Fastjson過程中閱讀了幾十篇中英文Fastjson
    Dubbo Kryo & FST RCE
    2022-11-25 15:31:47
    影響版本Dubbo 2.7.0 to 2.7.8Dubbo 2.6.0 to 2.6.9Dubbo all 2.5.x versions 環境復現 安裝zookeeper和dubbo-samples,用idea打開dubbo-samples-api,然后修改其中的pom.xml如下: 注意,dubbo-common必須 ≤2.7.3版本。在Dubbo<=2.7.3中fastjson的版本≤1.2.46 ,這也是我們這個洞的利用點,不過這里復現使用的更高版本所以需要添加依賴, com.alibabagroupId> fastjsonartifactId> 1.2.46version>dependency>. 案例漏洞分析 FTS反序列化FTS反序列化發生在RPC協議反序列化
    如何攻擊Java Web應用
    2021-07-12 08:50:42
    越來越多的企業采用Java語言構建企業Web應用程序,基于Java主流的框架和技術及可能存在的風險,成為被關注的重點。
    項目安裝迷你天貓商城是一個基于Spring Boot的綜合性B2C電商平臺,需求設計主要參考天貓商城的購物流程:用戶從注冊開始,到完成登錄,瀏覽商品,加入購物車,進行下單,確認收貨,評價等一系列操作。作為迷你天貓商城的核心組成部分之一,天貓數據管理后臺包含商品管理,訂單管理,類別管理,用戶管理和交易額統計等模塊,實現了對整個商城的一站式管理和維護。
    FastJson結合二次反序列化繞過黑名單
    Fastjson是阿里巴巴的開源JSON解析庫,它可以解析JSON格式的字符串,支持將Java Bean序列化為JSON字符串,也可以從JSON字符串反序列化到JavaBean。由于具有執行效率高的特點,應用范圍廣泛。
    Fastjson序列化遠程代碼執行漏洞 Fastjson 是一款開源的高性能 JSON 解析處理庫,在國內被廣泛使用。5 月 23 日,Fastjson 官方發布安全通告,聲明修復了一處新的反序列化漏洞: https://github.com/alibaba/fastjson/wiki/security_update_20220523
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类