您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關怎樣進行Fastjson 1.2.24反序列化漏洞分析,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
fastjson是一個非常流行的庫,可以將數據在JSON和Java Object之間互相轉換,但是在2017年官方主動爆出了fastjson的反序列化漏洞以及升級公告,這次我們就學習一下這個漏洞。
最終的payload會放到我的GitHub上。
這次使用的fastjson是1.2.23版本:
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.23</version></dependency>
在分析漏洞之前,我們先看下這個庫都有什么樣的功能,我們先創建一個User對象:
class User { private int age; public String username; private String secret; public int getAge() { return age;
} public void setAge(int age) { this.age = age;
} public String getUsername() { return username;
} public void setUsername(String username) { this.username = username;
} public String getSecret() { return secret;
}
}
我們主要關注一下從JSON還原回Object的方法,主要的API有兩個,分別是JSON.parseObject和JSON.parse,最主要的區別就是前者返回的是JSONObject而后者返回的是實際類型的對象,當在沒有對應類的定義的情況下,通常情況下都會使用JSON.parseObject來獲取數據。
fastjson接受的JSON可以通過@type字段來指定該JSON應當還原成何種類型的對象,在反序列化的時候方便操作。
String myJSON = "{\"@type\":\"me.lightless.fastjsonvuln.User\",\"age\":99,\"username\":\"lightless\",\"secret\":\"2333\"}";
JSONObject u3 = JSON.parseObject(myJSON);
System.out.println("u3 => " + u3.get("secret"));
如果需要還原出private成員的話,還需要加上Feature.SupportNonPublicField:
User u3 = (User) JSON.parseObject(myJSON, User.class, Feature.SupportNonPublicField);
根據官方的公告中的WAF檢測方法來看,問題很有可能是因為反序列化了任意類型的class從而導致的RCE。
從網上找到的payload中也能看出利用的是TemplatesImpl來執行的命令,這個在之前的JDK7u21中已經分析過了,可能還存在其他的執行命令方法,這些我們暫且不談,主要來看fastjson的部分。
我們搭建一個簡單的Web應用來接受用戶POST過來的JSON并且進行反序列化:
@RestControllerpublic class IndexController { @RequestMapping(value = "/fastjson", method = RequestMethod.GET) public String fastjson() { return "Hello World!";
} @RequestMapping(value = "/fastjson", method = RequestMethod.POST) public JSONObject testVuln(@RequestBody String data) {
JSONObject obj = JSON.parseObject(data, Feature.SupportNonPublicField);
JSONObject ret = new JSONObject();
ret.put("code", 1001);
ret.put("data", "Hello " + obj.get("name")); return ret;
}
}
傳入我們帶有@type類型的JSON字符串并且開始調試。在JSON.parseObject處下斷并開始向下跟。
一開始會跟進到JSON.parse方法,并且調用了parse()方法繼續進行JSON格式的匹配。繼續跟進parser.parse()方法。
到了這里之后,開始依次進行JSON的解析,我們傳入的第一個字符是{,所以進入LBRACE這個分支中,并繼續進入parseObject(object, fieldName)方法來解析對象。
這個時候lexer所在的字符為",會進入下面這個分支繼續解析JSON字符串,通過scanSymbol方法獲取到雙引號之間的字符串也就是@type
之后會獲取@type字段的值,并且嘗試獲取這個類的Class,經過一系列的判斷后,調用了deserializer.deserialize(this, clazz, fieldName)方法進行反序列化。
一開始沒找到sortedFieldDeserializers是在什么地方生成的,想仔細跟一下代碼,于是就從頭到尾的看了下getDeserializer(clazz)的部分,發現是在這里生成的。這個函數的目的是獲取一個可以反序列化我們通過@type指定的類的deserializer,由于預定于的列表中沒有,于是會繼續調用createJavaBeanDeserializer()來生成一個,實際上是調用了ParseConfig.build()方法,其中會通過反射機制獲取我們指定類的一些信息,通過對method進行一些過濾,猜測出getter和setter并推出一些可能存在的field。
具體的猜測規則這里不展開說明了,感興趣的話可以自行跟一下
緊接著就依次處理JSON字符串中的各個字段,當匹配到payload中的_tfactory字段的時候,由于我們傳入的JSON字符串中是一個空的對象,進入parseField方法后,繼續向下跟就會調用到JavaBeanDeserializer.deserialize()方法,在這里會為_factory創建一個TransformerFactoryImpl對象并賦值。
同樣的,_outputProperties字段我們也是傳入了一個空對象,會進入和上面相同的流程,仔細分析一下這段代碼,這里是觸發命令執行的關鍵部分。
首先依然會進入parseField方法
進入之后會調用smartMatch(key)方法,這個方法的主要作用是進行一些『智能匹配』,方便后續獲取對應變量的getter和setter。調用后這個方法會去掉字符串中的-、刪除開頭的下劃線等,所以當我們傳入了_outputProperties的時候,實際上就給處理成了outputProperties,并返回對應的FieldDeserializer對象,之后就會調用該對象的parseField方法。進入該方法后,就會調用setValue(object, value)方法,繼續跟進。
跟進之后顯而易見,getOutputProperties被調用了:
然后就會執行我們在_bytecodes構造的惡意字節碼,造成命令執行。
根據上面的流程,我們應該已經可以寫出PoC了,這里就不占用篇幅展示了,直接放到了我的GitHub上。
一開始在構造的時候發現字節碼中存在許多的不可顯字符,網上公開的PoC中使用了base64來編碼,感覺非常神奇,為什么fastjson會幫我們解碼呢?于是構造了數組傳入之后,發現fastjson在處理[B類型的數組時,會調用lexer.bytesValue(),其中的lexer就是JSONScanner,這個bytesValue()方法會自動幫我們執行一次base64解碼,所以我們構造payload的時候只需要傳入base64編碼后的內容即可。
至此,其實整個流程已經走完了,但是還有一點令我們非常難受,就是我們的demo中,在接收JSON的時候設置了Feature.SupportNonPublicField。默認情況下fastjson只會反序列化public的方法和屬性,而我們構造的PoC中有private的成員變量_bytecodes和_name,為了給這些變量賦值,則必須要假設服務端開啟了SupportNonPublicField功能。
而現實情況下,大部分都是parse(json)和parseObject(json)一把梭,使用這個功能的情況不是很多,這樣一來就導致我們的PoC沒有了良好的通用性,那么有沒有解決方案呢?答案當然是有的,那就是不使用TemplatesImpl,換一種RCE的觸發方式即可。在網上經過一番搜索,發現了一些可以利用的方法
主要是利用JNDI+RMI方法,這個可以參考之前的spring-tx.jar的反序列化問題,當時就是采用這種方式來觸發的。但是調用鏈不太好找,這個PPT中給出了多個利用鏈,這里的調用鏈后續再進行分析,這里暫且不做驗證。
這樣一來,只要是開發同學在編寫代碼時,直接反序列化了用戶傳入的JSON,就有可能造成RCE,而攻擊者也無需關心SupportNonPublicField是否開啟了,危害提高了許多。
在fastjson的官方補丁中,將loadClass(typeName, config.getDefaultClassLoader())替換為了config.checkAutoType(typeName),并且擴充了黑名單列表,將傳入的類名與黑名單一一比較,如果發現了相同開頭的類就停止反序列化。
// 新增的黑名單
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework
可以看到絕大部分常用的類都已經被加進來了,但是如果不經常維護此名單,一旦后面出現了新的可以利用的類,很容易就繞過這個限制。
上述就是小編為大家分享的怎樣進行Fastjson 1.2.24反序列化漏洞分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。