Many security vulnerabilities are found in libraries used by application code. When it’s impractical to quickly deploy a fix to code in a library, you may be able to use ModSecurity to intercept an exploit, “virtually patching” the affected code until you can upgrade the affected libraries.
The Apache Struts application library vulnerability (CVE-2017-5638), which led to the breach of 143 million accounts at Equifax, is an example of exploit that can be virtually patched. The signature of the vulnerability is the presence of #cmd=
or #cmds=
strings in the Content-Type
, Content-Disposition
, or Content-Length
HTTP headers. (For more details, see below.)
Using ModSecurity, we can create a virtual patch with a simple rule that searches for the malicious strings in the affected HTTP headers:
SecRule REQUEST_HEADERS:Content-Type|REQUEST_HEADERS:Content-Length|REQUEST_HEADERS:Content-Disposition "@rx #cmds?=" "id:5638,auditlog,log,deny,status:403"
We define the rule with SecRule
, providing three parameters:
- The request headers to search for, in the form of three
REQUEST_HEADERS
variables OR’ed together - The PERL‑compatible regular expression (PCRE), as specified by
@rx
, that searches the specified request headers for strings including#cmd=
or#cmds=
- The action to take
If ModSecurity is configured in active blocking mode, it drops any traffic that matches the PCRE and so triggers the rule.
Learn how to get started using NGINX and ModSecurity together with our ebook: ModSecurity 3.0 and NGINX: Quick Start Guide
Why Virtual Patch?
In a lot of cases it’s quicker to deploy a rule in ModSecurity than to patch the affected code, re‑test, and then deploy to production.
Consider the Apache Struts vulnerability as an example: because Struts is an application library and not an operating system package, updating it in an enterprise production environment can take some time. As part of upgrading to a new version of Struts, each Struts‑dependent application needs to be rebuilt and tested with the latest Struts library. A large organization might have hundreds of applications, each with its own version of the Struts application library, making it vulnerable until every single application is updated.
With the ModSecurity custom rule in place, you can then patch the production software carefully, and on a reasonable schedule, without the pressure of being vulnerable. Once all the affected software is updated, the custom rule can be decommissioned.
How the CVE-2017-5638 Exploit Works
Apache Struts CVE-2017-5638 is a remote command execution (RCE) vulnerability. This type of vulnerability allows the attacker to run arbitrary commands, such as /bin/bash
or cmd.exe
, on target systems. With that ability, the attacker can then search the file system and the network for sensitive data, with the same level of access as the Java application server. For example, if the Java application server is running as root
, then the attacker has root
privileges on the target system.
According to the official CVE, the vulnerability occurs when an attacker sends a malformed Content-Type
, Content-Disposition
, or Content-Length
HTTP header. Apache Struts throws an exception when those HTTP headers don’t match any of the expected values. The problem occurs because the exception‑handling code attempts to print the unescaped, invalid header. (In this context, “unescaped” means that the suspect commands are not prepended with characters that prevent them from being executed – escape characters – as is normally done when printing code.)
The attacker can put an Object Graph Navigation Library (OGNL) expression into the Content-Type
header. OGNL has the ability to run system commands. When the unescaped, invalid header is printed, the OGNL expression is evaluated, and any system commands within the OGNL expression are executed.
Exploits typically are a variation on the curl
command below.
curl -H "Content-Type:%{(#_='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='ls -ltr').(#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())}" www.example.com
The key syntax is in the second half of the command: one instance each of the strings #cmd=
and #cmds=
(highlighted above). Each of the strings is followed by the system command to run.
Summary
The preferred solution is always to patch vulnerable software right away. But patching production software can be time‑consuming, and rushing updates can be risky. Creating a virtual patch for vulnerable software with ModSecurity can buy time.
With virtual patching, you create a custom ModSecurity rule to block traffic that might exploit the security vulnerability, such as CVE-2017-5638. By doing so, you protect your site from the attack. You can then patch the production servers carefully, and on a reasonable schedule, without the fear of being victimized in the meantime.
If you’d like to learn more about ModSecurity and the NGINX WAF, please download our ebook, ModSecurity 3.0 and NGINX: Quick Start Guide.
Resources
- CVE-2017-5638 – The official CVE notice
- CVE-2017-5638: Anatomy of the Apache Struts Vulnerability – A valuable overview of the vulnerability
- Apache Struts Security Bulletin S2‑045 – Apache Struts page on the security vulnerability, with a workaround
- User comment on the ArsTechnica forum user comment – Explanation why patching Apache Struts is not easy