On March 7, 2017, the Apache Struts team disclosed CVE-2017-5638, a critical remote code execution flaw in the Jakarta Multipart parser that let an attacker run arbitrary commands by embedding a malicious OGNL expression inside an HTTP Content-Type header. No authentication, no user interaction — just a crafted header. Equifax was running a public-facing dispute-portal application built on the vulnerable Struts version, and by mid-May 2017, attackers were inside its network exploiting exactly that flaw. The intrusion ran undetected until late July, and by the time Equifax finished its investigation, the breach had exposed Social Security numbers, birth dates, and addresses for approximately 147 million people — one of the largest consumer data breaches in U.S. history, confirmed in Equifax's own congressional testimony as originating from the unpatched Struts vulnerability. What makes the Equifax story more than a one-time failure to patch is that it isn't isolated. Apache Struts, a Java web framework with roots going back to 2000 and still in active use inside enterprise and government systems today, has produced a recurring line of critical CVEs over more than a decade, including CVE-2018-11776 and CVE-2023-50164 — bugs that, on inspection, trace back to the same handful of architectural decisions. This post walks through that pattern and what it means for teams still running Struts-based applications.
What exactly was CVE-2017-5638, and why was it so severe?
CVE-2017-5638 lived in the Jakarta Multipart parser Struts 2 used to handle file uploads. The parser passed the value of the Content-Type header into an error-handling routine that evaluated it as an OGNL (Object-Graph Navigation Language) expression — Struts' built-in expression language for binding HTTP parameters to Java object properties. Because the header value reached OGNL evaluation without being treated as untrusted input, an attacker could embed an expression that invoked Runtime.exec() directly, achieving remote code execution with a single unauthenticated HTTP request. There was no login wall to bypass and no chained exploit required — the vulnerability was rated critical, reflecting complete compromise with no privileges needed. The Apache Struts security team shipped a patch on March 7, 2017. Equifax's own security process failed to apply it before attackers found the same public-facing endpoint roughly ten weeks later, which is the detail that turned a patchable framework bug into a landmark breach.
Was CVE-2017-5638 a one-off design flaw?
No — it was the clearest instance of a design pattern Struts had already shown before and would show again. Two years earlier, Struts' S2-016 advisory (CVE-2013-2251) documented another OGNL injection path, where unvalidated action: and redirect: parameters were evaluated as executable expressions. Both bugs share the same root mechanism: OGNL was built to let Struts read and write Java object graphs conveniently from HTTP request data, and that convenience only stays safe if every input reaching the evaluator is strictly validated first. Struts' interceptor-based request pipeline was designed in the early 2000s to make that binding automatic and low-friction for developers — a reasonable design goal at the time, but one that created a wide, reusable attack surface every time a new parameter-handling feature was added to the framework in the years since.
Did the pattern continue after Equifax?
Yes. In August 2018, CVE-2018-11776 (S2-057) showed that under certain configurations — namely, when the alwaysSelectFullNamespace setting was enabled and results lacked a defined namespace value — Struts would evaluate attacker-controlled URL/namespace input as an OGNL expression, again yielding remote code execution without authentication. And in December 2023, Apache disclosed CVE-2023-50164 (S2-066), a path-traversal flaw in the framework's file-upload logic: insufficient validation of the uploaded file's parameters allowed an attacker to manipulate the upload path, which could be leveraged to place a malicious file where it would later execute. Different subsystem, same underlying theme — Struts' upload and parameter-binding interceptors trusted request-supplied values further than they should have. Apache rated it critical and urged immediate upgrades, echoing the same urgency language used for CVE-2017-5638 six years earlier.
What's the common thread across these Struts CVEs?
Three recurring root causes show up across Struts' most severe advisories: evaluating user-controlled data as an executable expression (OGNL injection, as in CVE-2017-5638 and CVE-2013-2251), deserializing untrusted data through plugins like the REST plugin's XStream handling, and insufficient sanitization of file paths and filenames during upload handling (as in CVE-2023-50164). None of these are exotic bugs — they're the same handful of "untrusted input reaches a powerful sink" mistakes OWASP has cataloged for two decades. What's notable is that they kept recurring inside one framework's codebase across major version lines, because the interceptor and expression-binding architecture that made those mistakes possible in 2013 was still substantially present in 2023.
What does Struts' history teach about long-lived Java frameworks generally?
It teaches that framework age and prior CVE count are themselves signal, not just noise. A framework carrying 20+ years of interceptor, plugin, and expression-language design decisions accumulates attack surface that a newer, narrower framework simply doesn't have yet — every legacy feature is a potential entry point that has to be re-audited every time a new bypass technique is found. That's not an argument to panic-migrate off Struts; many of these CVEs are effectively fixed once patched and upgraded promptly. It's an argument for treating "this dependency has a long CVE history in a shared subsystem" as a standing risk factor that deserves faster patch SLAs and continuous monitoring, not a one-time upgrade-and-forget response — because the same OGNL and upload-handling code paths that produced CVE-2017-5638 are exactly where the next Struts advisory is statistically most likely to land.
How Safeguard Helps
The Equifax breach wasn't a detection failure — the vulnerable Struts dependency was known and patchable for weeks before attackers found it. That's a supply-chain visibility and speed problem, and it's exactly what SCA and continuous scanning are built to close. Safeguard's dependency scanning resolves Maven and Gradle manifests — including pom.xml, build.gradle, and lockfiles — up to 100 levels deep, so a transitive Struts dependency buried under an internal shared library or plugin doesn't hide from the scan the way it can with tools that stop at shallower depths. And because Safeguard's feed-driven rescoring evaluates every tracked component against new NVD, GHSA, and OSV entries continuously rather than only at build time, a newly disclosed Struts CVE like CVE-2023-50164 gets matched against your dependency graph and surfaced as a scored finding in minutes, not whenever the next scheduled scan happens to run.