flawopen.com/Incidents/Equifax / Apache Struts

Equifax: a two-month-old patch, 147 million people

Critical — 147M records CWE-917: Expression Language Injection Breach May–July 2017, disclosed September 2017
ELI5

A building's mail room accepts packages and reads the label to decide where to deliver them. Someone works out that if you write a sentence of instructions on the label instead of an address, the mail clerk carries them out. The lock manufacturer sent a fix for this two months earlier. Nobody in the building knew that particular mail room existed, so nobody installed it.

Key terms on this page
OGNL
Object-Graph Navigation Language — an expression language used inside Apache Struts to read and write properties on Java objects. It can invoke arbitrary methods, which is what makes injection into it fatal.
Content-Type header
An HTTP header describing the format of a request body. In this flaw it was parsed as an expression when it was malformed, rather than merely rejected.
asset inventory
A maintained record of which software runs where. Its absence is what turned an available patch into an unapplied one.

What happened

In September 2017 Equifax — one of the three major US consumer credit bureaus — disclosed a breach affecting approximately 147 million people. The exposed data included names, Social Security numbers, dates of birth, addresses and in some cases driver's licence and credit card numbers. Because Equifax collects this data from lenders rather than from consumers directly, the affected population had no relationship with the company and no ability to opt out.

The initial entry point was CVE-2017-5638, a remote code execution flaw in the Jakarta Multipart parser of Apache Struts 2. The Apache Software Foundation had published a patch on 7 March 2017. Equifax's dispute portal was still unpatched when attackers exploited it beginning around 13 May 2017. The intrusion continued undetected for roughly 76 days.

Timeline
7 March 2017
Apache publishes the Struts advisory and a fixed release. Exploitation in the wild begins within days.
8–9 March 2017
Equifax circulates an internal notice to apply the patch. The affected dispute portal is not identified or remediated.
13 May 2017
Attackers exploit the unpatched portal and begin moving through the environment.
29 July 2017
Equifax detects the intrusion after renewing an expired certificate on a traffic-inspection device, which restored visibility into encrypted traffic.
7 September 2017
Public disclosure.

The technical root cause

An error path that evaluated attacker input as an expression

When the Jakarta Multipart parser encountered an invalid Content-Type header, it built an error message incorporating the offending header value — and that message was then processed by OGNL. Because OGNL can invoke methods, an attacker who controlled the header controlled the expression, and could call out to the runtime to execute operating system commands.

The structural resemblance to Log4Shell four years later is striking and worth dwelling on: in both cases, untrusted data reached a general-purpose expression evaluator by way of a diagnostic code path. Error handling and logging are precisely where developers stop thinking about input validation, because the request has already been judged invalid.

THE SHAPE OF THE FLAW
// Conceptual illustration, not Struts source.

// A malformed header value is folded into
// an error message...
String msg = "Invalid content type: "
           + request.getHeader("Content-Type");

// ...which is then evaluated as OGNL.
ognlEvaluate(msg);

// Attacker sends a Content-Type containing
// an OGNL expression that invokes the
// runtime and executes a shell command.
THE PRINCIPLE
// Diagnostics must treat input as inert data.

String msg = "Invalid content type";
log.warn("{} value={}", msg, headerValue);

// Never interpolate untrusted values into
// anything that will later be parsed,
// evaluated, or executed — including
// error strings.

Why the patch was never applied

The technical flaw is unremarkable; the organisational failure is the reason this breach is still taught. Congressional and regulatory findings identified a cluster of causes that compounded one another:

The lessons that actually transfer

Equifax is the canonical demonstration that vulnerability management is an inventory problem before it is a patching problem. The organisation knew about the CVE, had the patch, and issued the instruction — and still lost 147 million records, because the instruction could not be mapped onto a system nobody had recorded.

The second durable lesson is about monitoring integrity. A security control that has silently stopped working is worse than a missing one, because it produces false assurance. An expired certificate on an inspection appliance is a mundane operational defect that, in this case, cost 76 days of detection time.

FAQ

Was this a sophisticated attack?

The initial exploit was not. CVE-2017-5638 was public, widely weaponised, and being scanned for at internet scale within days of disclosure. What distinguished the incident was the length of undetected access and the breadth of data reachable once inside.

How fast do I actually need to patch?

For an internet-facing, unauthenticated RCE in a widely deployed framework, the practical window is days, not weeks — mass scanning typically begins within 24–72 hours of public disclosure. The prerequisite is knowing where the software runs, which is why inventory work pays off more than patch-speed targets alone.

Related reading

Sources