Backstory
Magento's Web API is declarative. A developer registers a service class in a few lines of XML and the framework exposes its methods over REST and SOAP, converting the JSON request body into the PHP objects those methods expect. The class that performs the conversion first shipped on 31 January 2014 as ServiceArgsSerializer and was renamed to Magento\Framework\Webapi\ServiceInputProcessor on 13 February 2015, months before Magento 2.0 reached general availability. Its _createFromArray method walks the incoming array, and when a property expects another class rather than a string or an integer, it calls itself and builds that object too. That recursion is present in the 2.0.0 source.
In April 2018 a community pull request, magento2#14801, extended the pattern. Its stated goal was code style: "WebAPI interfaces were relying on setter/getter methods violating the immutable stateless pattern. This Pull Request enables the ServiceInputProcessor to get arguments directly from constructor." The new method, getConstructorData, filled constructor arguments from request data and applied no type restriction. It shipped in Magento 2.3.0 in November 2018. From then on a request body could reach internal classes through constructors as well as setters.
Untrusted input had reached code execution through this surface before. In 2016, Netanel Rubin described the setter path in the terms it still works by: "Magento lets you create an object, set its public properties, and execute any method starting with the 'Set' prefix through its RPC." His chain (CVE-2016-4010) ended in a native unserialize() call on a payment field. Adobe's answer was to remove PHP's deserializer from the codebase, a campaign that closed in April 2017 with a ticket titled "Verify all serialize/unserialize usages eliminated" and shipped in 2.2.0. CVE-2022-24086 reached unauthenticated code execution through checkout anyway, and entered the CISA catalog in February 2022. In June 2024, CosmicSting (CVE-2024-34102) reached ServiceInputProcessor itself. Researchers chained constructor parameters on an unauthenticated guest-cart endpoint until they instantiated SimpleXMLElement, converting object hydration into XXE. Their writeup stated the general condition: "This provides a huge surface for bugs. By traversing chains of constructors and setters, it is possible to instantiate a wide variety of internal classes." Adobe's fix was a two-class denylist in _createFromArray, rejecting subclasses of SimpleXMLElement and DOMElement.
SessionReaper walked a different chain through the same machinery, ending at Magento's customer session objects so that a PHP session file was written with attacker-influenced contents. Magento's default session backend is the filesystem: SaveHandlerInterface declares const DEFAULT_HANDLER = 'files', and app/etc/di.xml registers only db and redis as alternatives, so a stock install keeps sessions as PHP-serialized bytes that the framework later unserializes.
Technical analysis (revisited)
The original 9 September 2025 note recorded the bug as improper input validation reachable without authentication, which is what Adobe's advisory said. The mechanism became public six weeks later.
Searchlight Cyber's Assetnote research team published the root cause on 22 October 2025: getConstructorData recursively hydrated attacker-supplied JSON into PHP object graphs without restricting which classes could be instantiated. A crafted body sent to the unauthenticated guest-cart order endpoint traverses Magento\Quote\Api\Data\CartInterface and the customer session proxy chain, and terminates in a write to Magento's session storage. SonicWall's Capture Labs teardown reached the same conclusion, noting the input path is shared across the REST, GraphQL and SOAP surfaces: getConstructorData "fails to adequately validate complex nested objects, leading to unsafe deserialization."
Sansec, which named the bug SessionReaper, documented the second half of the chain. Attackers write PHP payloads as fake session files through the unauthenticated /customer/address_file/upload endpoint, and the deserialization primitive relocates the session save path so those files are read back as sessions. Observed payloads used GuzzleHttp\Cookie\FileCookieJar. Two constraints follow from this and were not in the original note: the remote code execution vector "appears to require file-based session storage," so Redis-backed installations are not exploitable by this chain, and Adobe "did not patch the possibility for arbitrary file uploads" (Sansec).
Adobe's fix changed strategy from denylist to allow-list. The commit adds five lines inside getConstructorData that skip any parameter that is not a simple type or an Api\Data class. The 2024 CosmicSting denylist still sits about forty lines below it in the same file. Adobe documented the consequence for integrators: "Service classes and complex models are no longer automatically instantiated from API payloads," and warned that extensions passing complex types over REST would break.
Lifecycle timeline
- 2025-07-17: MITRE CVE record reserved (source)
- 2025-08-20: Fix committed to the public magento2 repository as "WebAPI Enhancement", without reference to a CVE (source)
- 2025-08-22: Pre-release hotfix inadvertently exposed by Adobe (source)
- 2025-09-04: Adobe privately distributes the emergency fix to selected Commerce customers (source)
- 2025-09-09: Adobe publishes APSB25-88 out of band, rated Priority 2, with isolated hotfix VULN-32437-2-4-X-patch (source)
- 2025-09-09: NVD entry published, CVSS 3.1 base 9.1, CWE-20 (source)
- 2025-09-23: Tenable Nessus plugins 265750 and 265751 published (source)
- 2025-10-14: Fix ships in the scheduled releases 2.4.4-p16, 2.4.5-p15, 2.4.6-p13, 2.4.7-p8, 2.4.8-p3 and 2.4.9-alpha3 (source)
- 2025-10-22: Searchlight Cyber publishes the root-cause analysis (source)
- 2025-10-22: First in-the-wild exploitation observed and blocked (source)
- 2025-10-24: Added to the CISA Known Exploited Vulnerabilities catalog (source)
- 2025-10-24: Qualys publishes detections QID 733319 and QID 530559 (source)
- 2025-10-27: Akamai reports independent exploitation telemetry (source)
- 2025-10-31: Rapid7 InsightVM check added (source)
- 2025-11-14: CISA remediation date (source)
Real-world outcome
Nothing happened for forty-three days, and then everything happened at once.
The analyst note, dated 9 September 2025, recorded no known public exploit code and expected the window before active exploitation to be very short. The leaked hotfix had been available since 22 August, three weeks before the note was written, and no exploitation was observed in that period. The first attacks arrived on 22 October 2025, the same day Searchlight Cyber published its technical analysis. Sansec reported blocking more than 250 attempts from five source IP addresses that day, delivering PHP web shells and phpinfo probes, and noted that 62 percent of Magento stores were still unpatched six weeks after the fix. Akamai observed more than 300 attempts against over 130 distinct hosts from 11 IP addresses in the following 48 hours.
Scale escalated across the next ten days in Sansec's dated updates: attacks had reached 31 percent of all Magento stores by 24 October, 49 percent by 26 October with an estimated 16 to 18 percent carrying at least one injected backdoor, and 81 percent had been probed by 1 November. CISA added the CVE to its Known Exploited Vulnerabilities catalog on 24 October, two days after the first observed attacks, with SSVC values of exploitation active, automatable yes, technical impact total. Sansec reports Adobe raised the advisory from Priority 2 to Priority 1 the same day. No victim organisation has been publicly attributed to this CVE in any primary source, and CISA records ransomware campaign use as unknown.
Lessons
Two things are visible in this case that are not visible in the CVSS score.
The first is that the trigger for mass exploitation was a research publication, not the patch and not the leak. The fix was in the public repository from 20 August 2025 under the message "WebAPI Enhancement", and a hotfix had escaped two days later, yet exploitation began on the day a readable root-cause walkthrough appeared. Silent patching bought Adobe roughly two months of quiet, and merchants who treated the Priority 2 rating as permission to wait spent most of that window unpatched.
The second is what the file shows about incremental fixes to a general design. Adobe removed PHP's native unserialize() from Magento in 2017 and that worked: the 2016 gadget class is gone. What remained was the framework's own deserializer, a method that builds arbitrary PHP object graphs from attacker-supplied JSON on pre-auth endpoints. CosmicSting in 2024 was closed with a denylist of two XML classes. SessionReaper in 2025 used the same recursive hydration to reach a different class entirely, and both patches now sit about forty lines apart in ServiceInputProcessor. The pattern here is that enumerating dangerous destinations does not constrain a mechanism that will instantiate whatever the request body names; the 2025 fix works differently because it inverts the question, permitting only simple types and Api\Data interfaces. The breaking-change warning Adobe attached to that fix is the price of the years the permissive behaviour was part of the contract.
References