Apache Struts2 has a long, uncomfortable history with OGNL (Object-Graph Navigation Language) injection, and CVE-2020-17530 is one of the clearest examples of why partial fixes to injection classes tend to resurface. The vulnerability allows an unauthenticated attacker to achieve remote code execution by supplying a malicious OGNL expression that gets "force" evaluated when it is used as the value of certain tag attributes — no authentication, no special privileges, just a crafted HTTP request against a vulnerable endpoint. For any organization still running an affected Struts2 branch, this is a full server compromise primitive, not a theoretical weakness.
What the vulnerability actually does
Struts2's tag library supports OGNL expressions inside tag attributes so that dynamic values can be rendered from the value stack (for example, id="%{someValue}"). Under normal handling, Struts evaluates these expressions safely against the constrained value stack context. The bug tracked as CVE-2020-17530 is that certain tag attributes undergo forced double evaluation of OGNL — meaning a value that should be treated as a plain string gets evaluated as an OGNL expression a second time. If that value originates from unsanitized user input (a request parameter, for instance), an attacker can inject an OGNL payload that, on the second evaluation pass, escapes the value stack sandbox and reaches Java runtime methods — enabling arbitrary command execution on the host.
This is architecturally the same failure family as CVE-2017-5638 (the Equifax breach vulnerability) and CVE-2019-0230: Struts' OGNL evaluation layer keeps finding new code paths where attacker-controlled strings get treated as executable expressions rather than inert data. CVE-2020-17530 specifically traces back to an incomplete resolution of that earlier class of double-evaluation bugs — the framework closed one door and left an adjacent one ajar.
Affected versions and components
- Affected: Apache Struts 2.0.0 through 2.5.25
- Fixed in: Apache Struts 2.5.26
- Advisory: S2-061 (published by the Apache Struts security team)
- Component: The Struts2 tag handling / OGNL evaluation layer used when rendering tag attributes that are backed by user-supplied values
Any application built on a Struts2 version in the affected range is exposed if it uses tags whose attributes can be influenced by request data — which is a common pattern in real-world Struts applications (form tags, UI tags, and custom tags that echo parameters back into attribute values).
Notably, the fix shipped in 2.5.26 was later determined to be incomplete. Security researchers found additional forced-OGNL-evaluation bypasses against the patched code, which Apache tracked separately as CVE-2021-31805 (advisory S2-062) and resolved in Struts 2.5.29. Organizations that patched only to 2.5.26 and stopped there should treat that as a partial mitigation, not a closed issue — this is a case where "patched" and "patched for this specific CVE" are not the same statement.
CVSS, EPSS, and exploitation context
Public vulnerability databases list CVE-2020-17530 with a Critical-range CVSS score consistent with unauthenticated, network-exploitable RCE requiring no user interaction — the same severity band as the other headline Struts2 OGNL injection CVEs. As with most Struts2 RCEs, the combination of no authentication requirement, network attack vector, and full impact on confidentiality, integrity, and availability places it firmly in the "patch immediately" tier rather than the "schedule for next cycle" tier.
Struts2 OGNL vulnerabilities as a class have consistently attracted high exploitation interest once public proof-of-concept payloads circulate, because the vulnerable pattern (an OGNL expression like %{...} in a request parameter) is trivial to scan for at scale and the payoff is remote code execution. Security teams should treat any internet-facing Struts2 instance in the affected version range as an active target, and validate exposure through both dependency inventory and runtime behavior rather than assuming legacy internal systems are out of scope — Struts2 deployments frequently outlive the teams that originally built them, and unpatched instances tend to persist in exactly the systems nobody is actively watching.
Timeline
- Struts2's broader OGNL history: Multiple prior CVEs (most notably CVE-2017-5638 and CVE-2019-0230) established the recurring pattern of OGNL double-evaluation and forced-evaluation bugs in Struts' tag handling and Content-Type parsing paths.
- CVE-2019-0230 (S2-059): Apache addressed an OGNL double-evaluation issue, fixed in Struts 2.5.22 — this fix narrowed but did not fully close the forced-evaluation attack surface.
- CVE-2020-17530 disclosed (advisory S2-061): Apache Struts published the advisory describing forced OGNL evaluation via tag attributes, affecting 2.0.0–2.5.25, with the fix released in Struts 2.5.26.
- Follow-on discovery: Independent research subsequently found the 2.5.26 fix was incomplete, leading to a new CVE (CVE-2021-31805, advisory S2-062) covering additional forced-evaluation bypasses, resolved in Struts 2.5.29.
The throughline across this timeline is instructive: each fix closed the specific bypass that was reported, without eliminating the underlying design pattern that makes forced OGNL evaluation dangerous whenever tainted input reaches it. Teams tracking "Struts2 OGNL RCE" as a single line item in a vulnerability register risk missing that it is actually a recurring vulnerability class spanning several CVEs and several years.
Remediation steps
- Upgrade past the entire affected range, not just to the CVE's fix version. Given the follow-on incomplete-fix issue, upgrade to Struts 2.5.29 or later (or the current supported release) rather than stopping at 2.5.26. If you are running any Struts2 version below 2.5.29, treat this as unresolved exposure to the forced-OGNL-evaluation class.
- Inventory every Struts2 instance, including embedded and third-party ones. Struts2 is frequently bundled inside vendor products, internal tools, and archived projects. A dependency scan of your own source tree will miss instances shipped inside other software — supply chain visibility has to extend to what your vendors and internal tools are built on.
- Audit tag usage for user-controlled attribute values. Where feasible, avoid passing raw, unsanitized request parameters directly into tag attributes that support OGNL evaluation. Apply strict input validation/allow-listing on any parameter that can influence rendered tag output.
- Deploy compensating detections at the edge. WAF or reverse-proxy rules that flag OGNL syntax markers (
%{,#context,#_memberAccess, etc.) in request parameters, headers, and paths can catch exploitation attempts against instances that cannot be patched immediately — this is a mitigation, not a substitute for upgrading. - Review application logs for historical exploitation indicators. Because Struts2 OGNL RCEs are commonly automated and scanned for opportunistically, check for anomalous OGNL-pattern requests, unexpected outbound connections, or unfamiliar process spawns from the Struts2 application server around the time the instance became exposed.
- Re-verify after patching. Given the history of incomplete fixes in this exact vulnerability class, confirm the deployed version and validate — ideally with a safe, non-destructive proof-of-concept check — that forced OGNL evaluation is actually blocked post-upgrade, rather than trusting the version number alone.
How Safeguard Helps
CVE-2020-17530 is a textbook case for why point-in-time dependency scanning isn't enough: the "fixed" version wasn't actually fixed, the vulnerable pattern spans multiple CVEs across years, and the components affected are often buried inside vendor software your team didn't build and doesn't directly track. Safeguard is built for exactly this kind of supply chain reality.
Safeguard continuously maps your software composition — including transitive and vendor-embedded dependencies — so a Struts2 instance hiding three layers deep in a third-party tool shows up in your inventory instead of your blind spot. When advisories like S2-061 (and its follow-on S2-062) are published, Safeguard correlates them against your actual deployed versions in real time, flags instances that were only partially remediated (like teams that stopped at 2.5.26), and prioritizes them using exploitability and exposure context rather than CVSS score alone. For vulnerability classes with a track record of incomplete fixes, Safeguard's tracking persists past the first patch, re-checking affected components against subsequent related CVEs so recurring issues like Struts2's OGNL evaluation problems don't quietly reopen after teams consider them closed. That closes the exact gap that let CVE-2020-17530's fix — and its unpatched successors — linger in production environments long after the advisory was public.