Struts security app-ocalypse. IT security manager’s survival guide

Recently, we have seen one of the most spectacular data breaches in history. Equifax has lost about 143 million records of personal and credit information data (roughly 1/3 of the US population).

Sławomir Jasek 2017.09.19   –   9 MIN read

The problem

Recently, we have seen one of the most spectacular data breaches in history. Equifax has lost about 143 million records of personal and credit information data (roughly 1/3 of the US population).

In their official statement, they  confirmed that the reason was an unpatched vulnerability in the Struts framework. It is also not a secret any more that unpatched vulnerability in the framework had a significant role in one of the biggest server heists in Polish banking.

The sad landscape I pointed out over 5 years ago in my talk “No Man’s Land: Vulnerabilities in J2EE Web Application Frameworks”, unfortunately did not change a lot for better in the meantime. There are more and more bugs following more, ready to use exploits, but when it comes to addressing the problem, it seems still to be a “no man’s land”.

One thing may have changed a bit though: it seems that the frameworks finally acquired the deserved attention of security researchers, resulting in more frequent advisories on critical vulnerabilities, and – unfortunately – real life attacks hitting the news one after another.

The lineup of Struts 2 vulnerabilities is impressive. There are also multiple publicly available exploits, gaining full access to server (the holy grail for the attacker – “Remote Code Execution”) – including Metasploit modules, scripts, and automated scanners plugins. The threat is real and the risk looks serious. So, how do we deal with it?

Am I vulnerable?

Answering this obvious question may not be as trivial as it seems, and even solid vendors with established security incident response management cannot provide an easy answer. For example, several days after disclosure, Cisco in its advisory on recent Struts vulnerabilities is still “investigating its product line to determine which products may be affected”.

And even if your application is built upon Struts, and even its affected version, that does not automatically mean it is exploitable in your case. In most cases the vulnerability exploits specific framework functionality or construction in code. Your application may just not utilize the vulnerable feature. That is the case for the recent S2-052 (CVE-2017-9805) – the non-default REST plugin has to be enabled. It is also the case for S2-053 (CVE-2017-12611) – your developers had to use non-standard (considered wrong) construction of freemarker tags in their code. Unfortunately for S2-045 (CVE-2017-5638)  (apparently exploited in Equifax), exploited functionality is widely implemented.

Moreover, most of publicly available exploits use a series of special characters and constructions. Consider this example payload, exploiting the Equifax CVE-2017-5638 or another one for  CVE-2017-9805. It’s obviously not even remotely close to a typical input from your users. Properly configured Web Application Firewall – especially using a “whitelist” approach – can prevent such an attack by definition. But beware: default of the shelf box configuration may not help at all. On the contrary – may give you a false sense of security.

The use of an automated vulnerability scanner may reveal the problem, but only in the most obvious cases and with a bit of luck. Sometimes a specific path or some manual payload tweaks are required for a successful exploit, and even an experienced pentester can miss it.

The best way to check vulnerability potential is a configuration review.  So, let’s get back to finding out if your application uses the Struts, and most importantly – which version – using this approach.

First, you need to figure out, whether your application uses the affected libraries. As the Struts is quite a popular J2EE framework, chances are high that if your application is written in Java technology, the developers implemented it in Struts. A simple look at your application in a browser may be helpful – if the URL paths contain “.do” or “.action” – it is a pretty good indicator that the Struts was used. But the Struts app can also have standard URLs. From the outside, you also won’t know exactly which version of the framework is running.

You can simply ask your developers which version they compiled in, and then look it up for example in prior releases vulnerabilities table in Struts 2 downloads section. But the used version may change in time and depend on the build environment. So the developer’s answer may differ from the binary actually installed.

In order to investigate the version actually running on your server, you’ll need some help of your web application server administrator.  The J2EE application is usually delivered as WAR or EAR pack file (or multiple files). The file format is just a simple ZIP. Inside, look for external libraries – they are in a WEB-INF/lib subfolder. In most cases, just listing of this folder is enough to find out used libraries along with their versions. An example: content of WEB-INF/lib in a Struts2 REST showcase application (https://archive.apache.org/dist/struts/2.5.10/struts-2.5.10-apps.zip) vulnerable among others to CVE-2017-9805:

(…)
ognl-3.1.12.jar
struts2-config-browser-plugin-2.5.10.jar
struts2-convention-plugin-2.5.10.jar
struts2-core-2.5.10.jar
     struts2-rest-plugin-2.5.10.jar
(…)

as you can see, it uses Struts2 REST plugin, version 2.5.10 – which according to Struts S2-052 advisory is vulnerable. In order to fix it – upgrade to 2.5.13 is needed. If you see struts2-core, but not struts2-rest-plugin – this specific exploit won’t affect your site (others might).

You can also perform this task with the help of several products. Especially worth noting is a free and opensource OWASP Dependency Check. It can automatically identify used libraries, and point out their vulnerabilities. An example: run of command-line dependency check against the same vulnerable Struts2 REST showcase application:

dependency-check.sh –project test -s struts2-rest-showcase.war
[INFO] Checking for updates
[INFO] Skipping NVD check since last check was within 4 hours.
[INFO] Check for updates complete (6 ms)
[INFO] Analysis Started
[INFO] Finished Archive Analyzer (0 seconds)
[INFO] Finished File Name Analyzer (0 seconds)
[INFO] Finished Jar Analyzer (0 seconds)
[INFO] Finished Central Analyzer (33 seconds)
[INFO] Finished Dependency Merging Analyzer (0 seconds)
[INFO] Finished Version Filter Analyzer (0 seconds)
[INFO] Finished Hint Analyzer (0 seconds)
[INFO] Created CPE Index (1 seconds)
[INFO] Finished CPE Analyzer (1 seconds)
[INFO] Finished False Positive Analyzer (0 seconds)
[INFO] Finished Cpe Suppression Analyzer (0 seconds)
[INFO] Finished NVD CVE Analyzer (0 seconds)
[INFO] Finished Vulnerability Suppression Analyzer (0 seconds)
[INFO] Finished Dependency Bundling Analyzer (0 seconds)
[INFO] Analysis Complete (37 seconds)

The resulting report includes a list of identified components, their versions, and vulnerabilities:

In our case the tool did point out several critical vulnerabilities in the Struts (including the dangerous CVE-2017-5638). Despite syncing to the latest version NVD CVE vulnerabilities feed, it did not however properly identify the most recent ones – including aforementioned CVE-2017-9805 and CVE-2017-12611. This results out of the quality of the NVD feed, which does not cover the most recent ones yet. Manual verification is therefore recommended (note: commercial vendors claim to address it automatically but double check their claims).

What to do?

As soon as you confirm your application uses a vulnerable framework version, proceed to patching it. Should be easy, right? No, unfortunately not.

Due to the way the applications are built, the library is embedded in a binary pack delivered by your developers. Patching it is beyond the scope of responsibility of your server administrators. There is no simple upgrade mechanism; the fix does not come in a system package update. An administrator cannot also just switch off the affected internal file manually – the application most likely will crash.

The only way to patch is to prepare a new application build. In most cases the need for that is overlooked by developers, as they do not actively track public disclosures of vulnerabilities in used libraries. You will have to step in to the “no man’s land” yourself, and enforce the update build – as only a developer can do it.

The process is supposed to be painless – frameworks should be backwards compatible, and simple library upgrade should not have negative impact on the application functionality. But unfortunately no developer will guarantee it 100 %, especially if the upgrade applies to a significant number of versions up. For example, the application may use a non-standard feature, which was not considered crucial for backwards compatibility, and there is a slight chance that after such an upgrade something goes wrong. That would most likely be a result of bad coding and failure to oversee the update necessity – fault of the application developers. But expect raising questions from your teammates – who will be responsible for the functional testing after the upgrade.

Regardless the update process – which may not be straightforward, and will take some time – consider implementing another layer of defence in the meantime – WAF (Web Application Firewall). The exploit payload is usually complex and uses a series of special characters and suspicious strings, which can be easily blocked. Of course, a skilled and determined attacker may find a way to bypass such filters, especially if you use the “blacklist” blocking approach. Therefore, it would be best to accept only “whitelist” of validated parameters from user’s browser – along with their expected values, size, and character sets. Otherwise, at least fine-tune the filter to properly match the recent exploits. As previously mentioned, an automatic solution with default configuration out of the box may not be able to help you. Such solution was most probably installed at Equifax, some speculations on this matter are already in the press.

For some of the vulnerabilities, you can also implement workaround by simply disabling the affected functionality. For example, you can mitigate the CVE-2017-5638 by disabling, or switching file upload interceptor (https://struts.apache.org/docs/s2-046.html). Turing off unused features according to the least privilege principle is a good security practice by the way.

Preventive actions

The most obvious first step, if you haven’t done so yet, is to catalogue external libraries (along with their versions) used by your applications. That will allow you to track their vulnerabilities.

In case of development outsourcing, it may be wise to clarify the responsibility and terms for library upgrades in the agreement with your development vendor.

If you develop the application yourself, you can easily incorporate proper tools (commercial or the free and opensource OWASP Dependency Check) into your development lifecycle process – there are plugins for Jenkins, Gradle, Maven, SBT… It may not be perfect, but hey – it’s opensource, so you are welcome to contribute. For .NET applications – check OWASP SafeNuGet.

And last but not least – implement multiple layers of defence – properly configured WAF, intrusion detection and logging, separation of privileges, server hardening, etc.

Summary

Despite the threat is real, the risk may not be so – it does not necessarily mean the vulnerability is possible to exploit in your case.

Employing best practices – establishing various security measures, setting up a process of continuous monitoring and quick patching of libraries used by your applications – can prevent such problems in the future. And as usual – automatization in SDLC is vital and definitely it will help you to raise the bar, but periodic manual security verification is essential for your application security.

Sławomir Jasek
Sławomir Jasek Principal IT Security Consultant