Iâm going to give away a secret that Iâve successfully used at every interview Iâve had for a security position.
âCross-Site Scriptingâ (XSS) is a remarkably poor term for the attack or vulnerability (code can be particularly vulnerable to a cross-site scripting attack) it describes. The correct term should be âHTML Injectionâ, because it succinctly explains the source of the problem, and should provide developers with pretty much all the information they need to recognise, avoid, prevent, and address the vulnerabilities that lead to these attacks being possible.
Particularly galling is when I look at code whose developers had heard about XSS, had looked about for solutions, and had found a half-baked solution that made them feel better about their code, made the bug report go from âreproducibleâ to âcannot reproduceâ, but left them open to an attacker with a little more ingenuity (or simply a more exhaustive source of sample attacks) than they had. It seems that developers often try, but are limited by the resources they find on the Intarweb â particularly blogs seem to provide poor solutions (ironic, I know, that I am complaining about this in my blog).
Alright then, hereâs something that appears to be new to many â a demonstration of Cross-Site Scripting without scripting.
We all know how XSS happens, right? A programmer wants to let the user put some information on the page. Letâs say he wants to warn the user that his password was entered incorrectly, or his logon session has expired. So, he needs to ask the user to enter username and password again, but probably wants to save time by putting the userâs name in place for the user, to save on typing.
Hereâs what the form looks like â youâve all seen it before:
Needles to say â donât use these as examples of good code â these are EXAMPLES of VULNERABLE CODE. And lousy code at that.
$query = new CGI;
print $query->h1("Session timed out."), $query->p("You have been logged out from the site - please enter your username and password to log back in."), $query->start_form(-action=>"happyPage.htm", -method=>"get"), "Username: <input name=\"username\" value=\"" + $query->param("username") + "\" />",$query->br(),
"Password: ", $query->input(-name=>"password",-type=>"password"),$query->br(), $query->submit(),
If we modify our URL by adding â#&â between the query and the variable âusernameâ, it demonstrates one of the more frightening aspects of DOM-based attacks. Those of you who are aware of what an anchor does to a browser will already have figured it out, but hereâs a quick explanation.
The âanchorâ is considered to be everything after the â#â in a URL. Although it looks like itâs part of the query string, itâs not. Browsers donât send the â#â or anything after it to the server when requesting a web page, so itâs not seen in a network trace, and itâs not seen in the server logs. This means that DOM-based attacks can hide all manner of nastiness in the anchor, and your scanners wonât pick it up at all.
So, this page would normally get executed with a parameter, âusernameâ which would be the username whose account weâre asking for credentials for â and certainly it works with http://localhost/XSSFile.htm?username=Fred@example.com :
The trouble is, it also works with the XSS attackersâ favourite test example, alert("XSS")%3b’>http://localhost/XSSFile?username="><script>alert("XSS")%3b</script> :
Now, Iâve seen developers who are given this demonstration that their page is subject to an XSS attack. What do they do? They block the attack. Note that this is not the same as removing or fixing the vulnerability. What these guys will do is block angle brackets, or the tag â<script>â. As a security guy, this makes me sigh with frustration, because we try to drill it into peopleâs heads, over and over and over again, that blacklisting just doesnât work, and that âmaking the repro go awayâ is not equivalent to âfixing the problem demonstrated by the reproâ.
The classic attackerâs response to this is to go to http://ha.ckers.org/xss.html for the XSS Cheat Sheet, and pull something interesting from there. Maybe use the list of events, say, to decide that you could set the âonfocusâ handler to execute your interesting code.
But no, letâs suppose by some miracle of engineering and voodoo the defender has managed to block all incoming scripts. Even so, weâre still vulnerable to XSS.
What happens if we try this link:
[The â%3dâ there is a hex value representing the â=â character so that the query-string parser doesnât split our attack.]
OK, thatâs kind of ugly â but it demonstrates that you can use an XSS vulnerability to inject any HTML â including a new <form> tag, with a different destination â âbadpageâ in our URL above, but it could be anywhere. And by hiding the attacked input field, we can engineer the user into thinking itâs just a display issue
With some piggery-jokery, we can get to this:
Looks much better (and with more work, we could get it looking just right):
So, there you have a demonstration of scriptless cross-site scripting. XSS, or HTML Injection, as Iâd prefer you think of it, can inject any tag(s) into your page â can essentially rewrite your page without your knowledge. If you want to explain the magnitude of XSS, it is simply this â an attacker has found a way to rewrite your web page, as if he was employed by you.
[Of course, if I hadnât been trying to demonstrate that XSS is a misnomer, and prove that you can shove any old HTML into it, I would simply have used a piece of script, probably on the onmouseover event, to set the action of the form to post to my bad site. Fewer characters. Doing so is left as an exercise for the reader.]
It doesnât work in Internet Explorer, but in other browsers, it seems to work just fine. At first look, this would seem to suggest that âinnerHTMLâ on a <form> tag is allowing the â</form">â in the XSS to escape out from the parent form. I can assure you thatâs not the case, because if you could escape out, that would be a security flaw in the browsersâ implementation of innerHTML. So, whatâs it doing, and how do you find out?