CSRF
Cross-site request forgery (CSRF) is a web vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform.
Fundamental Concepts
Same Origin Policy (SOP)
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.
The origin is defined as the combination of scheme, host, and port of a URL.
The SOP applies whenever two URLs differ in at least one of these three properties.
If a browser did not enforce the Same-Origin Policy, a web application (site A) could make cross-site requests to a separate web application (site B) using the userβs site B cookies and read authenticated responses!
It is crucial to understand that the Same-Origin Policy does not block requests, but prevents web applications from reading responses from another site.
Cross-Origin Resource Sharing (CORS)
Cross-Origin Resource Sharing (CORS) is a W3C standard that defines exceptions to the Same-Origin Policy, allowing a web application to specify which origins and HTTP methods are permitted to access its resources.
A web server can configure exceptions to the Same-Origin policy via CORS by setting any of the following CORS headers in the HTTP response:
Access-Control-Allow-Origin
SOP exceptions for a specific origin
Access-Control-Allow-Headers
SOP exceptions for or allowed HTTP headers in response to a preflight request
Access-Control-Allow-Methods
SOP exceptions for allowed HTTP methods in response to a preflight request
Access-Control-Expose-Headers
SOP exceptions for specific HTTP headers
Access-Control-Allow-Credentials
if set to true, define Same-Origin policy exceptions even if the cross-origin request contains credentials, i.e., cookies or an Authorization header
Access-Control-Max-Age
define for how long the information in the other CORS-headers can be cached without issuing a new preflight request
Preflighted Requests
The most straightforward CORS configuration is that of a so-called simple request, which can be made from plain HTML, without any script code. Simple requests can be GET or HEAD requests without any custom HTTP headers.
All requests that do not fall under the simple requests conditions are called preflighted requests. Before sending these cross-origin requests, the browser sends a preflight request (HTTP OPTIONS method) to the different origin containing all the parameters of the actual cross-origin request. This enables the web server to decide whether to allow the cross-origin request. The browser waits for the response to the preflight request and only continues to send the actual cross-origin request if the web server allows it by setting the corresponding CORS headers in response to the preflight request. Since the browser requests permission from the web server before sending the actual cross-origin request, CSRF vulnerabilities with preflighted requests are impossible.
The preflight request is an OPTIONS request that contains the following headers:
Access-Control-Request-Method: inform the server about the HTTP method used in the actual request
Access-Control-Request-Headers: inform the server about the HTTP headers used in the actual request
CSRF Defenses (short)
Web applications use unique, random CSRF tokens to verify legitimate requests, check Origin and Referer headers to confirm the requestβs source, and set cookies with the SameSite flag to limit cross-site cookie use.
Tools and Resources
CSRF PoC Generator: https://csrf-poc-generator.vercel.app/
Setting an Attacker HTTP Server
Modern browsers implement security measures that prevent HTTPS websites from loading resources via unencrypted HTTP connections. To avoid running into issues, always use HTTPS requests.
To perform any CSRF exploitation, we need to host a webserver over HTTPS. To do that, you need a self-signed certificate for your server, which you can generate with:
openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodesModern browsers won't allow loading resources on a misconfigured HTTPS webserver using a self-signed certificate. This setup is just of testing purposes.
Then, you'll need a simple Python HTTPS server that configures CORS for incoming OPTIONS requests to enable POST requests from our payloads, and additionally logs incoming requests:
from http import server
import ssl
class CustomRequestHandler(server.SimpleHTTPRequestHandler):
def do_OPTIONS(self):
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def do_GET(self):
super().do_GET()
def do_POST(self):
length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(length)
if body:
self.log_message("[i] POST body: %s", body.decode("utf-8", errors="replace"))
self.send_response(200)
self.end_headers()
print("Serving HTTPS on 0.0.0.0 port 443 (https://0.0.0.0:443/) ...")
httpd = server.HTTPServer(('0.0.0.0', 443), CustomRequestHandler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile='./server.pem')
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
httpd.serve_forever()Basic Payloads
Send a GET request to https://yourtarget.com/vulnerable.php?param1=value1
Send a POST request to https://yourtarget.com/vulnerable.php with param1=value1
Send a POST request with two JSON parameters
Leveraging CORS Misconfigurations
Arbitrary Origin Reflection
The Access-Control-Allow-Origin header contains the origins allowed to bypass the Same-Origin policy, making the browser allow the origin to access the web app's response.
Identifying the misconfiguration
To identify a CORS misconfiguration that reflects arbitrary origins, edit your request and add any arbitrary domain to the request's Origin header, then, send the request and check if the domain is included in the Access-Control-Allow-Origin response header.
Sometimes, a web application might check an origin against a whitelist before reflecting it. If the whitelist checks for strings containing a prefix or suffix of an origin, it may be vulnerable.
The header can be set to a wildcard (*), which results in all origins being granted a Same-Origin policy bypass. Notice that, for security reasons, the wildcard origin cannot be combined with the Access-Control-Allow-Credentials: true
Note: A combination of origin and wildcard, such as https://*.example.com is not valid!
To exploit this misconfiguration, you will need to host the following payload on your attacker server:
You don't need to set the Origin header: the browser will automatically set it to your attacker server's domain.
Then, supposing a victim is authenticated to the misconfigured-cors web application, and navigates to your server's page where the payload is hosted, they will automatically send a request to the vulnerable web application and you will receive a request containing a base64 value corresponding to the web application's response.
This works because the response reflects the origin in the CORS header and allows credentials, meaning the attacker's origin is granted an exception to the Same-Origin policy. For example, if a web application only accepts origins containing "google.com", you can create a domain named "sfoffogoogle.com" and exploit the vulnerability.
If the server's Access-Control-Allow-Credentials header is set to true, you can potentially make authenticated requests in the victim's context, potentially gaining access to sensitive user-related information and allowing actions to be performed on behalf of the victim.
Trusted null origin
The Access-Control-Allow-Origin header not only supports a trusted origin and a wildcard but also the value null, which indicates the null origin. Although this should not be used in practice, some web applications may implement it due to a misconception of its meaning.
An attacker can employ various methods to force a null origin on a cross-origin request, which is subsequently trusted, resulting in a Same-Origin policy exception.
To exploit this, check whether the null Origin is reflected. If it works, you can enforce a null origin using a sandboxed iframe
This attack is an extension of the arbitrary origin reflection.
The previous exploit payload is the same as in the previous misconfiguration. However, the sandboxed iframe results in a null origin in the cross-origin request instead of using the attacker server as the origin.
Targeting internal Assets
Even if the web application does not configure CORS to allow credentials, an attacker might still be able to target web applications running in a local network behind a firewall, reverse proxy, or NAT that are not publicly accessible.
Data exfiltration may be possible if these do not require authentication and contain a CORS misconfiguration that trusts the attacker's origin.
The next payload can work if the victim opening it is in the same internal network as the internal asset:
Bypassing CSRF Tokens
If CORS is misconfigured to allow cross-origin requests with credentials (Access-Control-Allow-Credentials: true) and reflects an attacker-controlled origin via the Access-Control-Allow-Origin header, CSRF token protections can be bypassed, provided that the userβs session cookies are set with SameSite=None and Secure.
This allows an attacker to issue an authenticated cross-origin request to an endpoint that generates a valid CSRF token, read the token from the response, embed it into a subsequent state-changing cross-origin request, and successfully perform the action on behalf of the victim.
Since all requests are executed within the victimβs authenticated session, the CSRF token remains valid even if it is properly tied to the user session.
Other Misconfigurations
Bypassing SameSite Cookies
The SameSite cookie attribute is sent according to the request's source site attribute, instead of using the origin that is normally considered by the Same Origin Policy.
The key difference between site and origin is that the port and subdomain are not considered part of the site, meaning that two domains are considered the same site, even if the port and subdomain differ.
Bypassing Lax SameSite cookies
You can leverage the Lax declaration to circumvent restrictions imposed by SameSite cookies.
Lax SameSite cookies are only sent with safe requests, such as GET requests.
If the web application contains any endpoints that are state-changing and are accessed with GET requests, the SameSite protection is ineffective to prevent CSRF attacks.
Bypassing Strict SameSite cookies
Client-side redirections are initiated by the starting site, meaning that the redirection is considered SameSite and allows sending the victim's cookies with the request, even if they are set as Strict SameSite.
If you can redirect the victim to a misconfigured endpoint that accepts GET requests for state-changing operations, you can execute a successful CSRF attack.
Note: this bypass only works with client-side redirects, not server-side redirects such as HTTP 3xx status codes.
Weak CSRF Tokens
Simple bypasses to weak tokens can occur when:
the CSRF token is not tied to a user session: In that case, an attacker accessing the vulnerable web application can add a valid CSRF token to the cross-origin request from their own session.
CSRF tokens are not entirely random, or the generation algorithm is predictable: depending on how token is created, we might be able to guess it in a single attempt or brute-force it. For example, some tokens may be generated based on the current Unix Timestamp.