April 13, 2017 | Posted in Red Teams by Garrett Fails
What is Apache Struts?
Apache Struts (Struts) is an open-source framework used to create Java web applications. The great thing about Struts is that it allows developers to build powerful web applications using a well-established, portable language. The awful thing about Struts is that code execution vulnerabilities are frequently discovered and disclosed. CVE-2017-5638 is a recent example of this, but there is no shortage of vulnerabilities that can be used by an attacker to ruin your day. Point-and-shoot exploits are readily available for many of the more serious vulnerabilities:
Figure 1 – Example vulnerabilities from the Metasploit Framework
As with most software; especially software that is internet-facing, patching is the first line of defense against known exploits and Struts is no exception. Unfortunately, this is more challenging than you may expect.
What about ‘apt-get update && apt-get upgrade’ ?
The primary challenge in identifying instances of struts is that it is not managed via a package manager; It is distributed as a standalone JAR file. This means that there is no reliable way to query a built-in mechanism on a system (APT, RPM, etc.) to determine what versions are installed.
Pitfalls of identifying Struts from the Outside (Internet).
Vulnerability scanners (Nessus, Qualys etc.) are commonly used to scan and identify vulnerable web applications on a periodic basis by most of our clients. They can be useful in detecting Struts when you know where your web applications are and how they work. However, detection plugins do not exist for all known Struts vulnerabilities and those that do exist often rely on checking against Struts functionality that may be disabled.
Manual identification is potentially more accurate, but is extremely effort intensive to perform regularly.
The challenges of any approach that relies on examining web servers from the outside are:
- Struts may be present on more servers than are known about.
- Multiple versions of struts can exist on a single web server.
- Not all web applications will be inventoried
- Information disclosed to the outside world by the web server (library versions, etc.) cannot be trusted since reported versions can be unreliable or intentionally misrepresented.
- Some Struts detection and proof-of-concept exploits require application specific custom functions to be called.
The scan is coming from inside the building!
Since identification from the outside is prone to error, we approached the issue from a sysadmin standpoint. Ultimately, keeping Struts up to date is a patching problem. With that in mind we put together scripts that can be used to identify and parse Struts JAR files in a Linux environment.
With the assumption that privileged access to Linux web servers is possible, simply searching the filesystem to identify and versioning Struts has one major pitfall: Struts JAR filenames contain a version, but may not be accurate (Changed maliciously, changed to match a different version to avoid re-writing application code, etc.). While this scenario may not seem likely, it would leave you exposed if it occurred. If detection is going to be as accurate as possible, we need to do more than just check filenames. So, we created a proof of concept detection method that is portable and accurate.
Proof of Concept.
Our proof of concept consists of two scripts; one that sets up the environment and a second that performs the actual work. At a high level, the scripts search the entire file system of a target server and then parses any identified Struts JAR files to determine the version. They rely on having SSH access as a privileged user to each server that needs to be checked.
The scripts may require some modification depending on the target environments, however as written they’re reasonably portable and will work on most distributions without major changes.
- struts_check_wrapper.sh – this is the parent script that sets up the environment and runs struts_id.sh against each target.
- As input, it takes:
- Username with privileged access to the remote server
- SSH key file used to authenticate to the remote system
- A script to run against the targets (struts_id.sh in this case)
- An output directory to store the results of running struts_id.sh
- A file containing the target host
- When run, it connects to each target system over SSH, launches a shell and reads in the struts_id.sh file to be run on the remote system.
- Output is collected in individual text files, one for each target.
- The implemented approach avoids needing to upload anything to servers, everything is run in session.
- As input, it takes:
- struts_id.sh – this is the child script that looks for and extracts information from Struts JAR files.
- When run it:
- Searches the filesystem starting at the drive root for Struts files (in this case assumed to have “struts” in the name and have a .JAR file extension).
- For each matching JAR file, it unzips it to a pipe (JAR files are just zip files containing all the Java files) then greps a version number from the MANIFEST.MF file.
- Output is returned in a tab delimited format of:
- No files are left on disk when finished, nothing needs to be cleaned up.
- When run it:
- 52.89.x.x.txt – This is an example of what the output looks like.
- More or different information can be collected.
This general approach can also be used to identify and version all JARs on a system, not just Struts.
OK. How do I use it?
The scripts work well enough for one-off checks against an environment. This isn’t nicely repeatable without some additional work. Patching never ends; at best, you’re protected against the known vulnerabilities and that will probably change again very quickly. The more centralized and automated a process is the easier it is to manage, repeat and generate actionable results. You could go the traditional route with Cron jobs, output parsing and automated report generation. Infrastructure as Code tools (Chef, Ansible, etc.) could be used to apply the logic across swaths of systems if you’re fortunate and have a system in place. A contemporary infrastructure management platform like Tanium could be used to implement the logic as a repeatable question for easy querying and reporting across all your endpoints.
Where do I get it?
We’re releasing the scripts under the Apache 2.0 license. It is our hope that they’re a jumpstart to help you get a handle on Struts in your environment.
You can get the scripts from our GitHub here: https://github.com/SecurityRiskAdvisors/Struts-Checker
Resources we used while working on the proof of concept:
Apache Struts Project – https://struts.apache.org/
Vulnerabilities – http://struts.apache.org/downloads.html ( Prior Releases Table )
Prior Releases – http://archive.apache.org/dist/struts/
Java Package Versioning Specification: https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html#wp89936