XSS Through CSRF

Monday 18th Apr 16

I've got to start by saying that I don't claim that this technique is new or novel or that others haven't also been doing it for years but as I've been using it a lot recently I thought I'd put up a blog post about it.

Take a look at this snippet of PHP:

<?php
if ($_SERVER['REQUEST_METHOD'] == "POST") {
	print "Welcome: " . $_POST['name'];
}
?>
<form method="POST" action="welcome.php">
	Name: <input type="text" name="name" /><br />
	<input type="submit" value="Submit" />
</form>

If you don't know any PHP, a quick description for you. On a GET, a form is displayed asking the user to enter their name, on a POST, the name is displayed back without any sanitization. This is obviously vulnerable to a really basic reflected XSS, entering the following name will result in a nice little alert box:

<script>alert(1)</script>

XSS alert popup

While really basic, the question a lot of developers and users ask is "How would we get the JavaScript in there?". If the reflection was through a querystring parameter we could easily include it in a URL and have the victim click the link but through a POST parameter it is a little harder.

We might be able to come up with some cunning social engineering pretext where we tell the user that entering the "special" code will get them a cheaper deal on their shopping or let them see any other user's email, but that adds an extra step which is an extra potential point of failure or detection. This is where Cross Site Request Forgery comes in...

If you know anything about Cross Site Request Forgery (CSRF) then you'll be able to see that the form has no CSRF protection, this is what we will use to trigger the XSS.

The following is a typical CSRF exploit script, it contains a form with a name field, as in the original, but this time with the XSS code in the value field. The JavaScript triggers after the form has been rendered and automatically submits the form to the vulnerable page.

<form method="POST" action="welcome.php">
	<input type="hidden" name="name" value="<script>alert(1)</script>" /><br />
</form>
<script>
	document.forms[0].submit();
</script>

If this page is hosted on a site controlled by the attacker, all they need to do is to lure the victim to visit the page. Once the page is loaded, the form is submitted and the XSS triggered. This is a much easier attack than trying to convince a victim to enter the exploit code themselves.

A potential problem here is that the victim ends up on the vulnerable page, potentially exposing that something odd has happened. The pretext could be created so that the user expects this but if this isn't possible then some obfuscation is required, iframes are your friend here. This page contains some nice text to distract the victim but also an iframe holding the CSRF attack page.

<p>This is a really interesting page full of pictures of cute cats.</p>
<iframe style="opacity: 0.4;width: 200px;height: 200px" src="welcome_csrf.php" />

XSS iframe

In a real attack, the height and width would be 1px and the opacity would be 0, effectively hiding the iframe from view.

So there we have it, CSRF and a little framing combined to give a nice simple way to quietly exploit POST based reflected XSS, potentially without the victim being aware anything was done if that is required.

Defences

There are plenty of guides to protecting against XSS, these mainly involve encoding all user input before use and, in the more modern guides, enabling Content Security Policy (CSP) to block inline scripting.

To defend against these two extra additions to the attack there are two extra simple defences required. The first is to prevent the original site from being framed, this is done with the X-Frame-Options header. For a good write up on this, see this OWASP guide. The second is to add CSRF protection, again, OWASP have a good intro to CSRF and its defences.

Conclusion

The main aim of this article is to show that POST based XSS should not be ignored as hard to exploit, but I also hope that it helps to give you ideas of how combining attacks can make them much more potent. Quite a few people I've spoken to have said that they feel CSRF isn't really an issue but as has been shown many times, combining enough seemingly low level risks together can result in much more than the sum of the parts.