White Hats - Nepal

Security research, bug bounty writeups, pentest notes

Unmasking CSRF Attacks: A Pentester's Practical Guide

A Cross-Site Request Forgery (CSRF) attack, often pronounced "sea-surf," tricks a victim's web browser into sending an authenticated request to a vulnerable web application. This happens without the victim's explicit knowledge or consent, typically using their existing session cookies. The goal is to perform actions on behalf of the victim that they would normally be authorized to do, such as changing passwords, transferring funds, or making purchases, all from a malicious website.

From my years in penetration testing, I've seen CSRF vulnerabilities range from trivial informational leaks to critical account takeovers. Understanding how these attacks work isn't just academic; it's essential for anyone looking to secure web applications or find high-impact bugs.

Understanding the Mechanics of a CSRF Attack

At its core, a CSRF attack exploits the trust a web application places in a user's browser. When you log into a website, your browser stores session cookies. These cookies are automatically sent with every subsequent request to that site, authenticating you.

The Core Concept: Trusting the User's Browser

Imagine you're logged into your bank's website. Your browser holds a session cookie proving your identity. If, while still logged in, you visit a malicious website, that site can embed a hidden request to your bank. Because your browser automatically attaches your bank's session cookie to this request, the bank's server sees it as a legitimate request originating from you. The server has no way of knowing that the request was initiated by a third-party site rather than by you directly clicking a button on their own legitimate page.

Key Takeaway: CSRF exploits the browser's automatic inclusion of cookies and the web application's failure to verify the true origin of a state-changing request. The server trusts the cookie; the attacker trusts the browser to send it.

Anatomy of a CSRF Payload

Attackers use various methods to embed these malicious requests. The simplest forms involve HTML tags that automatically trigger HTTP requests.

GET-based CSRF Example

Many applications use GET requests for state-changing actions, which is a significant security flaw. Consider a money transfer function that looks like this:

GET /transfer?amount=1000&to=ATTACKER_ACCOUNT HTTP/1.1

An attacker could embed this within an image tag on their malicious site:

<img src="https://bank.example.com/transfer?amount=1000&to=ATTACKER_ACCOUNT" width="1" height="1" border="0" />

When the victim visits the attacker's page, their browser tries to load the image. In doing so, it sends the GET request to bank.example.com, including the victim's session cookie. The bank performs the transfer, believing the victim initiated it.

POST-based CSRF Example

POST requests are often considered safer, but they're still vulnerable if not properly protected. An attacker can craft a hidden HTML form that automatically submits itself:

<html>
  <body onload="document.forms[0].submit()">
    <form action="https://bank.example.com/transfer" method="POST">
      <input type="hidden" name="amount" value="1000" />
      <input type="hidden" name="to" value="ATTACKER_ACCOUNT" />
      <input type="submit" value="Click me to win a free iPhone!" />
    </form>
  </body>
</html>

This code either auto-submits via JavaScript or relies on social engineering to trick the victim into clicking the submit button. Again, the victim's browser sends the POST request with their session cookies, making the transfer.

Key Prerequisites for a Successful CSRF Attack

For a CSRF attack to succeed, several conditions must align:

  1. Authenticated Session: The victim must be logged into the target web application.
  2. State-Changing Request: The vulnerable action must involve changing the application's state (e.g., changing data, performing an action), not just retrieving information. While a GET request could theoretically change state, it's generally a bad practice and makes CSRF much easier.
  3. Predictable Request Parameters: The attacker needs to know the exact parameters and values required for the target action. If parameters are dynamic or session-specific (other than the session cookie), the attack becomes harder.
  4. Lack of CSRF Tokens: This is the big one. The absence of a unique, unguessable token in the request prevents the server from distinguishing legitimate user-initiated requests from forged ones.
  5. Browser Policy Limitations: The attack generally bypasses the Same-Origin Policy (SOP) because SOP prevents *reading* responses from different origins, not *sending* requests. The browser *will* send the request, but the attacker won't see the response.

Practical CSRF Attack Scenarios and Impact

The real danger of CSRF lies in its versatility. If a user can perform an action, an attacker might be able to force them to do it.

Account Takeover & Password Reset CSRF

This is one of the most critical impacts. Imagine a vulnerability on a "change email" or "change password" endpoint:

Financial Transactions & Data Manipulation

E-commerce sites and banking applications are prime targets:

Admin Panel Exploitation

If an authenticated administrator visits a malicious page, the impact can be devastating:

CSRF in API Endpoints

Modern web applications heavily rely on APIs, and these are not immune to CSRF, especially JSON-based APIs. JSON CSRF typically occurs when an application expects a JSON payload but doesn't strictly enforce the Content-Type header. If a server accepts JSON with a Content-Type like text/plain or application/x-www-form-urlencoded, an attacker can craft a form that sends a JSON-like string as the value of a single parameter.

<form action="https://api.example.com/update_profile" method="POST" enctype="text/plain">
  <input type="hidden" name='{"name": "Attacker", "email": "[email protected]"}' value='' />
  <input type="submit" value="Click me!" />
</form>

The server might then parse the request body as JSON, even with the incorrect content type. This bypasses typical browser protections that prevent cross-origin requests with `application/json` content types unless CORS is explicitly allowed.

Detecting CSRF Vulnerabilities: A Pentester's Workflow

Finding CSRF vulnerabilities requires a systematic approach, combining manual inspection with automated tools.

Manual Inspection: The First Line of Defense

I always start by manually examining every request that performs a state-changing action. This means looking at forms, button clicks, and AJAX requests.

  1. Look for Anti-CSRF Tokens: In forms, check for hidden input fields with names like _csrf, csrf_token, or similar. For AJAX requests, look for custom headers (e.g., X-CSRF-Token) or parameters in the request body.
  2. Analyze Request Methods: Are GET requests being used for state changes? That's an immediate red flag.
  3. Inspect `SameSite` Cookie Attributes: Check the `Set-Cookie` headers for session cookies. If they are set with `SameSite=None` (requiring `Secure` and often not vulnerable unless there's an older browser or other bypass), `Lax`, or `Strict`, it tells you something about the application's baseline protection.
  4. Parameter Predictability: Can you guess all the parameters needed for a specific action without knowing anything unique to the victim's session (other than the session cookie itself)?

Using Burp Suite for CSRF Detection

Burp Suite is your best friend here. It provides the tools you need to intercept, modify, and replay requests, which is crucial for testing CSRF.

Crafting Proof-of-Concept (PoC) Exploits

A good PoC clearly demonstrates the vulnerability. Here's a basic example for a POST request missing CSRF protection:

<html>
  <body>
    <h1>Free Bitcoins! Click to claim!</h1>
    <form action="https://vulnerable.example.com/change_password" method="POST">
      <input type="hidden" name="old_password" value="current_password" /> <!-- Often not needed if just setting new password -->
      <input type="hidden" name="new_password" value="AttackerNewPass123!" />
      <input type="hidden" name="confirm_password" value="AttackerNewPass123!" />
      <input type="submit" value="Claim your Bitcoins!" />
    </form>
    <script>
      // Optional: Auto-submit the form
      document.forms[0].submit();
    </script>
  </body>
</html>

To test this, save it as an HTML file (e.g., csrf_poc.html) on your local machine or a controlled web server. Log into vulnerable.example.com in your browser, then open csrf_poc.html in a new tab in the *same browser*. If the password changes, you've got a CSRF.

Bypassing CSRF Protections: Advanced Techniques

Developers often implement CSRF defenses, but these aren't always perfect. As a pentester, your job is to find the flaws.

Token Validation Bypass

Anti-CSRF tokens are the primary defense, but their implementation can be weak:

`SameSite` Cookie Attribute Bypass

The `SameSite` attribute helps mitigate CSRF by restricting when cookies are sent with cross-site requests. However, it's not a silver bullet: