Another day, another zero-day attack rears its ugly head. Today’s is brought to you by “the Jakarta-based file upload Multipart parser under Apache Struts 2”1 and security’s Rule Zero2, “Thou shalt not trust user input. Ever.”
In a nutshell, the attack exploits a vulnerability in an uploader component of the system, specifically by sending commands via the “Content-Type” HTTP header. According to El Reg3, researchers have seen some pretty nasty attacks. The short story is that this vulnerability allows remote execution which means it’s Grade-A, Super Duper Bad News.
A patch is available. If you’re running Apache Struts 2 and the vulnerable component, update now. Like, stop reading and update now. The advisory4 suggests this workaround, stating you should “validate Content-Type and throw away requests with suspicious values not matching multipart/form-data. (You can find more information about this advisory and how to mitigate this threat on DevCentral.)5
Now, the real reason I’m writing today is not necessarily to warn you about the vulnerability—you probably heard about it long before you hit this page, anyway. The reason I’m writing today is to remind you that HTTP Headers are user input, too.
Really. I know it seems odd, but they are. If you take a gander at this screen grab by Talos of a probing attack6 (one that’s checking if the system is vulnerable) you’ll see what I mean:
No matter how application-savvy you are, it should be fairly obvious that this is not a typical Content-Type header for an HTTP request. According to the RFC, Content-Type is usually of the form “type/subtype”7. This leviathan contains a valid Content-Type header in the very first line—multipart/form-data—but even a rudimentary BNF parser would flag this as a giant fail. But the thing is that HTTP headers, like form data and query parameters, are user input.
Let me repeat that: HTTP headers are user input.
That means I can put anything I want in the header and pass it to an application. And that means if you aren’t sanitizing those HTTP headers, you run the risk of exploitation. More often than not, it just breaks your application—undesirable, but not inherently dangerous. But on occasion, like this one, it’s a rather serious situation that could result in Very Bad Things™ happening.
Nick Biasini, who has a great blog on this vulnerability8, has some uber-scary screen grabs of what’s possible. None of which are desirable. None.
I cannot say it often enough: web application security is a stack. That stack includes protocols, of which HTTP is often the most prevalent. A comprehensive web application security strategy cannot ignore HTTP. It must view headers with the same skepticism with which user-generated input is processed, which is to say with a high degree of suspicion.
Remember, security’s Rule Zero is “Thou shalt not trust user input. Ever.” That must include HTTP headers, because at the end of the day, they are (or can be) user-generated. Postman, a popular tool for developers and DevOps for interacting with RESTful APIs, lets me do whatever I want to an HTTP request. Including bulk editing of headers, which means I can pretty much put what I want in there and see what happens.
Whether your system is vulnerable to an interestingly crafted HTTP header like Content-Type remains to be seen. However, this is not the first of its kind. The 2011 Apache Killer9 exploit was also based on exploitation of an HTTP header.
And because they are ultimately user input, HTTP headers should be treated as suspect and sanitized along with all other user input.
Security’s Rule Zero really is that foundational. A significant number of exploits can all be traced back to a failure to sanitize user input, in the headers and the body of an HTTP message. Adopting as absolute and adhering to this simple rule could go a long way toward making apps safer.