Apache Struts “serialisation” vulnerability – what you need to know

Update. The Struts 2.3 and 2.5 versions now both have official patches.
We have updated our advice below accordingly. [2017-09-07T11:00Z]

It seems only yesterday – in fact, it was six months ago – that we wrote about a nasty security hole in Apache Struts.

Unfortunately, it’s time for déjà vu all over again, with a similar sort of hole that can apparently be exploited in a similar way.

To explain.

Apache Struts is a software toolkit for creating Java-based web applications that run on your web server.

Struts can be used for building internet-facing services such as online shops or discussion forums: with Struts, you can generate web pages on the fly, tailor web content for the current user as they move around on your site, respond to web forms filled in by your visitors, and much more.

You can tell where this is going, given that an important part of any web application framework is dealing with the security risks implicit in requesting, acquiring and responding to data that is uploaded by outsiders.

And that’s where this Struts bug, known as CVE-2017-9805, comes in.

All applications should treat all input data as potentially hostile, even if it comes from an internal source that is supposedly under your own control. But when data comes from untrusted outsiders, you should go one step further, and assume that it is actively hostile – in other words, that it is booby-trapped in some way – unless and until you have good reason to think otherwise.

What’s the bug?

With a plugin called Struts REST, your web application can interact with visitors by means of Representational State Transfer (REST for short).

A REST-based web service – known colloquially as a RESTful service, because it is considered programmatically mellow – doesn’t have wild-looking URLs with data encoded into them like this:

Instead, there’s a standard URL for transactions to use, and the data needed for the web request or the reply is placed RESTfully in the HTTP body.

RESTful web requests are typically either GET (for example, to show a database record), PUT (to update a record), PATCH (to change one field in a record) or POST (to create new record), something like this:

Note that the REST process doesn’t specify how you’re supposed to represent, or encode, the data you want to send or receive – that’s up to you.

In the above example, we used basic HTML (more precisely, XHTML), but there are numerous ways to turn complex data structures into lines of text that are safe and suitable for a web request.

Popular formats include JSON (JavaScript Object Notation) and XML (Extensible Markup Language).

Struts REST supports XML using a programming library called XStream, which turns out to be way more powerful than is strictly necessary for exchanging data between browsers and servers.

Indeed, XStream allows you to encode any sort of Java object into XML (this is a technique with the fancy-sounding jargon name of serialisation, because it converts arbitrarily complex collections of structured binary data into a serial string of text characters), not just numbers and text.

How does this affect me?

As the XStream security documentation rather blandly explains:

By design, there are few limits to the type of objects XStream can handle. This flexibility comes at a price. […] The XML generated by XStream includes all information required to build Java objects of almost any type. This introduces a potential security problem.

[XStream XML data] can be manipulated by injecting the XML representation [of Java objects that aren’t supposed to be there]. An attacker could take advantage of this to execute arbitrary code or shell commands in the context of the server running the XStream process.

In short, any application that uses XStream, and thus, by association, any RESTful Struts application that uses XML for its data exchange, needs to take care not to allow crooks to feed it booby-trapped data.

Unfortunately, until Struts 2.5.13 (released on Tuesday 05 September 2017) and Struts 2.3.34 (released on Thursday 07 September 2017), booby-trapped XML could be fed to a Struts server so that attackers could embed commands into what was supposed to be plain data.

Indeed, a well-informed crook could present XML-encoded data to Struts that would be processed as commands during the decoding the data in the first place.

In other words, by the time your Struts app got to the point that it could validate the data it just received – for example, to check that it hadn’t been fed a fake phone number or given a website URL that was littered with exploits and malware – the damage might already have been done.

This sort of bug is known as RCE, or Remote Code Execution, and it generally means that crooks can take control of your server automatically from afar.

For example, crooks could carry out an operation that looked innocent – a product search or a stock-level check, say – but that deliberately triggered a malicious side-effect such as tricking your server into leaking data, acting as a distribution point for malware, or opening up a hole to let the crooks back into your network later.

What to do?

The discoverers of this vulnerability claim to have a “simple working exploit” as a proof of concept.

They claim that “[a]t the time of the announcement [2017-09-05] there is no suggestion that an exploit is publicly available, but it is likely that there will be one soon,” implying that crooks may figure out their own tricks easily enough.

  • If you use Struts, patch immediately. Struts versions from 2.1.2 to 2.3.33 inclusive and from 2.5 to 2.5.12 inclusive are affected. Official patches are available for the Struts 2.3 and Struts 2.5 versions.
  • If you use Struts but aren’t using REST, remove the Struts REST plugin. This reduces your attack surface area by giving crooks less to aim at in future.
  • If you use Struts REST but don’t use XML, turn off XML support. Apache lists the configuration setting you need (ironically, the configuration is itself specified using XML) so that the Struts REST plugin will work only with plain web pages or with JSON data, neither of which are processed by XStream.
  • If you use third-party web hosting or development services, ask your providers if they’re patched. You can outsource your operations, but not your accountability.
  • For future web applications, consider sticking to JSON instead of XML for data exchange. Programming libraries that handle JSON aren’t immune to security problems like this one, but the comparative simplicity of JSON compared to XML often makes it a better choice because it reduces your attack surface.

Regular readers of Naked Security will know that when it comes to boosting security by stripping down the plugins you install, restricting the options you enable, and simplifying the technologies you choose…

…we often end up quoting Mr Miagi from The Karate Kid: Best way to avoid punch – no be there.