Safeguard
Vulnerability Analysis

Struts2 REST plugin XStream deserialization RCE (CVE-2017-9805)

CVE-2017-9805 lets attackers RCE Struts2 REST APIs via unsafe XStream XML deserialization. Learn impact, affected versions, and remediation steps.

Nayan Dey
Security Researcher
7 min read

A remote code execution vulnerability in the Apache Struts2 REST plugin, tracked as CVE-2017-9805, lets an unauthenticated attacker execute arbitrary code on a vulnerable server by sending a single crafted XML payload to any REST-enabled endpoint. The flaw sits in how the REST plugin's XStream-based content handler deserializes incoming XML: it hands attacker-controlled data straight to XStream without any type whitelisting, so a malicious payload can instantiate arbitrary Java objects and chain them into command execution — no valid session, no authentication, no user interaction required. Because Struts2 powers a large share of enterprise Java web applications, and the REST plugin is a common choice for API-driven services, this vulnerability produced one of the more consequential waves of internet-wide scanning and exploitation in the Struts2 vulnerability history, and it remains a live risk on unpatched or forgotten legacy deployments today.

What's actually broken

The Struts2 REST plugin supports multiple content types for request and response bodies, including XML. To handle XML, the plugin registers an XStreamHandler that feeds incoming request bodies into the XStream library's fromXML() method to convert them into Java objects. XStream, by design, is capable of instantiating essentially any class on the classpath during deserialization unless the caller explicitly restricts which classes are permitted. The REST plugin's handler did no such restriction.

That gap means an attacker can POST (or PUT) a specially crafted XML document — using gadget chains built from common libraries like Java's ProcessBuilder-invoking constructs — to any action that accepts XML content, and XStream will happily construct and execute that gadget chain during deserialization. The result is unauthenticated remote code execution with the privileges of the application server process, delivered in a single HTTP request. There's no multi-step attack, no credential requirement, and often no visible error before the payload fires.

It's worth distinguishing this from the better-known CVE-2017-5638 (the Jakarta Multipart parser OGNL injection flaw associated with the Equifax breach). Both are Struts2 RCEs disclosed in 2017, but they are distinct bugs in different components — CVE-2017-5638 abuses OGNL evaluation in the multipart file-upload parser, while CVE-2017-9805 abuses unsafe XStream deserialization in the REST plugin. Teams doing exposure inventories should track them separately, since patching one does nothing for the other.

Affected versions and components

  • Component: Apache Struts2 struts2-rest-plugin
  • Vulnerable versions: Struts 2.1.2 through 2.3.33, and Struts 2.5 through 2.5.12
  • Fixed versions: Struts 2.3.34 and Struts 2.5.13
  • Trigger condition: The application must have the REST plugin enabled and expose at least one action mapped to a REST-style endpoint capable of accepting XML request bodies (the default content-type negotiation for the plugin includes XML alongside JSON).

The practical exposure surface is any Struts2-based application — internal admin panels, customer-facing APIs, partner integration endpoints — that pulled in struts2-rest-plugin for RESTful action mapping. Because the plugin is often added specifically to expose APIs to external clients, affected endpoints are disproportionately likely to be internet-facing.

Severity, exploit probability, and known exploitation

  • CVSS v3 Base Score: 8.1 (High), reflecting network attack vector, no privileges required, no user interaction, and high impact to confidentiality, integrity, and availability.
  • EPSS: This CVE has historically scored in the upper percentile of exploitation-probability rankings among scored vulnerabilities, consistent with the large volume of internet-wide scanning and automated exploitation activity observed against Struts2 REST endpoints since disclosure. Treat any Struts2 REST deployment still on an affected version as a near-term exploitation risk, not a theoretical one.
  • CISA KEV: Struts2 deserialization RCEs, including this vulnerability class, have a well-documented history of active, in-the-wild exploitation and appear on threat intel feeds and exploitation trackers used to prioritize patching across the industry. Organizations subject to CISA Binding Operational Directive-style patching SLAs, or any vulnerability management program that ingests KEV-equivalent exploitation signals, should treat this CVE as a must-patch item rather than a routine backlog ticket.

The combination of unauthenticated network access, no user interaction, and a single-request exploit path is exactly the profile that drives mass automated exploitation — attackers don't need to target you specifically; broad internet scanning finds exposed REST endpoints for them.

Timeline

  • Discovery: The vulnerability was identified and reported to the Apache Struts security team by Man Yue Mo of the Semmle Security Research Team (the group behind the lgtm.com code-scanning service), as part of a broader research effort auditing XStream usage patterns across popular Java frameworks.
  • September 5, 2017: Apache publicly disclosed CVE-2017-9805 and released Struts 2.3.34 and 2.5.13 with the fix, which restricts XStream's permitted types via a converter allowlist rather than allowing arbitrary class instantiation.
  • Following weeks: Public proof-of-concept exploit code and technical write-ups circulated widely, and security researchers and threat actors alike began mass-scanning the internet for exposed Struts2 REST endpoints — a now-familiar pattern for high-severity Struts2 disclosures.
  • Ongoing: Because Struts2 applications are frequently embedded deep in legacy enterprise stacks with long upgrade cycles, exploitation attempts against this CVE (and its sibling Struts2 deserialization bugs) continue to surface in honeypot and IDS telemetry years after the original disclosure — a reminder that "old" CVEs in unmaintained Java monoliths remain very much active attack surface.

Remediation steps

  1. Upgrade first. Move to Struts 2.3.34, Struts 2.5.13, or later. This is the only complete fix — it replaces unrestricted XStream deserialization with a converter that enforces an explicit class allowlist. There is no supported way to fully close this hole on an older Struts version without upgrading the framework itself.
  2. If you cannot upgrade immediately, disable XML content-type handling on REST actions. Restrict the REST plugin's accepted content types to JSON only by overriding the content-type-handler configuration, removing XStreamHandler from the handler chain. This eliminates the vulnerable code path without requiring a full framework upgrade, though it should be treated as a stopgap, not a permanent fix.
  3. Audit for REST plugin usage across your estate. Many organizations don't have an accurate inventory of which internal applications include struts2-rest-plugin as a transitive dependency pulled in by a shared internal framework or template. Generate or refresh your software bill of materials (SBOM) for every Java service and search specifically for this artifact and its version.
  4. Deploy compensating network and WAF controls. Block or alert on inbound requests to REST endpoints with Content-Type: application/xml or text/xml bodies containing suspicious XStream gadget markers (e.g., <java.beans.EventHandler>, <java.lang.ProcessBuilder>, or similar class-reference tags). This buys time during patch rollout but should never substitute for the upgrade.
  5. Segment and monitor. Internet-facing Struts2 applications should sit behind reverse proxies with strict method/content-type filtering, and application servers should be monitored for anomalous outbound connections or child process spawning — both classic post-exploitation indicators for successful deserialization RCE.
  6. Re-scan after remediation. Confirm the fix by re-running dependency and dynamic scans against the patched build, and verify that any WAF rules added as stopgaps are removed or retained deliberately rather than left as silent, unmaintained shims.

How Safeguard Helps

Safeguard is built to catch exactly this class of vulnerability before it becomes an incident, and to cut through the noise once it's found. Our SBOM generation and ingestion pipeline continuously inventories every Struts2 artifact across your services — including transitive pulls of struts2-rest-plugin buried inside shared internal libraries — so you know your exposure before an attacker does. Reachability analysis then determines whether your actual code paths invoke the vulnerable XStream handler on a network-reachable REST action, letting your team separate "must patch this week" from "present in the dependency tree but never actually exercised," instead of treating every hit as equally urgent. Griffin AI, Safeguard's reasoning engine, correlates the CVE's exploitation profile — unauthenticated, single-request, actively scanned in the wild — against your specific deployment topology to prioritize remediation automatically. And when it's time to act, Safeguard can generate auto-fix pull requests that bump affected Struts2 modules to patched versions, so the fix ships as fast as the finding.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.