Why this matters now
On October 27, 2026, CISA added CVE-2026-48356 to its Known Exploited Vulnerabilities catalog, with a remediation deadline of November 10 — tomorrow. The flaw, which the security firm Sansec named PolyShell, lets anyone on the internet upload an executable file to a Magento or Adobe Commerce store through its public shopping-cart API, with no login at all. Sansec saw the first probes on March 16, 2026, the first real attacks on March 19, and a mass wave on March 30 that planted a malicious JavaScript loader on 471 stores in a single hour. The campaign never stopped: in October 2026 the same operator was still rewriting store content, this time under a fifth loader domain, deepveindrilling.com.
| CVE | CVE-2026-48356 ("PolyShell") |
|---|---|
| CVSS | 9.3 Critical (NVD) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N. Adobe's bulletin rates it 9.6, the same vector except availability (A:L instead of A:N); this article leads with NVD's 9.3 and notes the difference. |
| Weakness | CWE-434 — Unrestricted Upload of File with Dangerous Type |
| Affected product | Adobe Commerce 2.4.4–2.4.9 (pre-July patch levels), Magento Open Source 2.4.6–2.4.9, Adobe Commerce B2B, Adobe Commerce Events 1.6.0–1.20.0 |
| Vendor | Adobe |
| Disclosure | Sansec advisory March 17, 2026; Adobe bulletin APSB26-73 and CVE publication July 14, 2026 |
| CISA KEV | Added October 27, 2026; federal remediation due November 10, 2026. Known ransomware use: Unknown. |
| Fixed in | July 2026 security releases — 2.4.9-2026-jul, 2.4.8-2026-jul, 2.4.7-2026-jul, 2.4.6-2026-jul, 2.4.5-2026-jul, 2.4.4-2026-jul (matching Magento Open Source and B2B builds); Commerce Events 1.21.0 |
| Exploitation | Confirmed exploited in the wild — Sansec observed attacks from March 19, 2026; 471 stores compromised in one hour on March 30, 2026; loader campaign still active in October 2026. Adobe's July bulletin said it knew of no in-the-wild exploitation at the time; Sansec had already documented it, and CISA's KEV listing confirms it. |
PolyShell matters because the vulnerable surface is the storefront itself. Every internet-facing Magento or Adobe Commerce store exposes the guest-cart REST API — it is how guest checkouts work — so there is no VPN, no admin login, and no perimeter control standing between an attacker and the upload. Sansec reports the flaw dates back to the first Magento 2 release, and that by April 14, 2026, malicious PHP had been uploaded to 82 percent of the stores it monitors. The endgame is payment data: once the webshell is in, the attacker injects a JavaScript loader into the store's CMS content, and every shopper's browser then runs attacker code — the classic Magecart pattern, dressed up as a traffic-distribution system.
The deadline is also what makes today the right day to hunt. CISA's November 10 due date applies to federal agencies, but the KEV listing is the signal every other Magento operator should act on: if your store missed the July patch, assume it was probed, and hunt for the webshells and loaders before you trust the checkout again. Patching alone is not a clean bill of health — Sansec found the October loader on a store running Magento 2.4.7-p10 with no missing core patches, reached through a rogue admin account created in September and a GSocket backdoor. The hunts below find the upload, the webshell, and the loader, in that order.
The technique, in hunt terms
PolyShell is a validation failure in Magento's REST API. When a product has a custom option of type file, the guest-cart item endpoints accept a file_info object — base64 file data, a MIME type, and a filename — as part of a cart item. Magento writes that file to pub/media/custom_options/quote/ without checking the option ID, the product's option type, or the file extension. Worse, the API accepts the file for any product, even one with no file option, from anyone, with no login. The attacker uploads a polyglot file — PHP code disguised as an image — which passes whatever extension checks exist and then executes as a webshell when requested.
From there the chain is consistent across Sansec's reporting, and it is what the hunts are built around. First, the webshell: attackers drop accesson.php backdoors across multiple directories, and stash further webshells disguised as PNG images in the upload folders. Second, persistence in the database: the attacker writes a JavaScript loader into the store's CMS blocks — the cms_block table — so the malicious script is rendered into storefront pages. Third, the loader: the injected script stores a payload in the visitor's localStorage (keys fpCode, 136c1e07507f4a97, 136c1e07507f4a98) and fetches the next stage from a loader domain. Five loader domains have rotated through this campaign since July 2025 — manulfie.click, neswnsssn.click, lanhd6549tdhse.top, additionalver.com, and deepveindrilling.com — and the October loader fingerprints the visitor's browser before deciding what to serve. Earlier stages used the Keitaro traffic-distribution system and served, among other things, a fake Ledger wallet page from leqdger.click.
In MITRE ATT&CK terms, the chain maps to T1190 (Exploit Public-Facing Application) for the unauthenticated REST upload, T1105 (Ingress Tool Transfer) for the webshell itself, T1505.003 (Server Software Component: Web Shell) for the persistent PHP backdoors, T1027 (Obfuscated Files or Information) for the polyglot files and base64-encoded loader URLs, T1189 (Drive-by Compromise) for shoppers' browsers loading the injected loader, T1071.001 (Web Protocols) for the loader domains and Keitaro callbacks, and T1078 (Valid Accounts) for the rogue admin accounts Sansec found on re-compromised stores. The post-exploitation pattern — a web shell on an internet-facing application, then persistence in content the application serves — is the same shape as the WordPress core RCE hunt: a WordPress upgrade flaw that left shells and rogue admins behind. If you ran that hunt, these queries will feel familiar.
A note on what is a hunting hypothesis and what is confirmed: the upload endpoint, the pub/media/custom_options/quote/ landing path, the accesson.php backdoor name, the loader domains, and the cms_block injection are all documented by Sansec. The exact guest-cart request an attacker used against your store is not something any public report can give you — so Hunt 1 looks for the pattern (bursts of guest-cart item POSTs from one source), not a single tell-tale URI, and the triage section tells you how to confirm a hit against the filesystem.
Hunt 1: The upload — guest-cart POST bursts and files landing in pub/media
PolyShell's entry point is the guest-cart item API: /rest/<store>/V1/guest-carts/<cartId>/items. A single POST there is normal — it is how guest checkouts add items. The signal is volume and repetition from a source that is probing: the March wave was fully automated, and Sansec describes mass scanning following the first attacks. This Splunk search counts guest-cart item POSTs by source over 10-minute buckets. Replace index=web and sourcetype=access_combined with your own web-log index and sourcetype; the field names follow Splunk's Common Information Model for web access logs.
index=web sourcetype=access_combined earliest=-14d http_method=POST uri_path="*/V1/guest-carts/*/items"
| bucket _time span=10m
| stats count as posts, dc(uri_path) as distinct_carts by _time, clientip
| where posts > 20
| sort - posts
If your WAF or reverse proxy logs request bodies, the upload itself has a marker the access log cannot show: the file_info object inside the POST body. This second Splunk search finds it — but only where body logging is on, which is uncommon, so treat a lack of results here as absence of logging, not absence of attack.
index=waf earliest=-14d "file_info" uri_path="*/V1/guest-carts/*"
| stats count as hits, values(clientip) as sources, earliest(_time) as first_seen, latest(_time) as last_seen by uri_path
| sort - hits
On the host itself, the upload is a file write. This CrowdStrike Falcon LogScale query looks for files written under the Magento upload directories by the web server, with an executable extension — the write that the API should never produce. It needs the Falcon sensor on the store host, and the paths assume a standard Magento layout; adjust the base path if your document root differs.
#event_simpleName=FileWritten
| TargetFileName=/pub\/media\/(custom_options|customer_address)\//
| TargetFileName=/\.(php|phtml|phar)$/i
| table([@timestamp, ComputerName, UserName, FileName, TargetFileName, ImageFileName])
| sort(@timestamp, order=desc)
Hunt 2: The webshell at work — PHP served from the media folder
Magento serves product images and uploads from pub/media/. It never legitimately serves a .php, .phtml, or .phar file from there. Any 200 response to such a request is a webshell executing, and any request for accesson.php anywhere on the host is the campaign's known backdoor name. This Splunk search is the highest-fidelity query in the article — tune the threshold to zero and investigate every hit.
index=web sourcetype=access_combined earliest=-14d
(uri_path="*/media/*.php" OR uri_path="*/media/*.phtml" OR uri_path="*/media/*.phar" OR uri_path="*accesson.php")
| stats count as hits, dc(clientip) as sources, values(status) as status, earliest(_time) as first_seen, latest(_time) as last_seen by uri_path
| sort - hits
The webshell's job is to run commands as the web server user. This CrowdStrike query catches the moment a web worker process spawns a shell or a network tool — the shape of a webshell being used, whether it arrived through PolyShell or another flaw. The same pattern, web server spawning a shell, is the core of the server-side template injection hunt; if that query is already tuned in your environment, reuse its allowlist here.
#event_simpleName=ProcessRollup2
| ParentBaseFileName=/^(php-fpm|httpd|apache2|nginx|php-cgi)$/
| FileName=/^(sh|bash|dash|perl|python3?|curl|wget|nc|ncat|socat)$/
| table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName])
| sort(@timestamp, order=desc)
Hunt 3: The loader, the skimmer, and the second foothold
The loader is what turns a compromised store into stolen payment data, and it is also the longest-lived indicator: the five loader domains have been reused across waves for fifteen months. Shoppers' browsers fetch them, so look for the domains in proxy and DNS logs — a hit means a visitor's browser loaded attacker code from your storefront, which means the injection is (or was) live. This Splunk search covers all seven domains Sansec has attributed to the campaign, including the two minor ones.
index=proxy OR index=dns earliest=-30d
| where match(url, "lanhd6549tdhse\.top|deepveindrilling\.com|additionalver\.com|manulfie\.click|neswnsssn\.click|hdfdsi84j3h\.top|c85grt98sd\.com") OR match(query, "lanhd6549tdhse\.top|deepveindrilling\.com|additionalver\.com|manulfie\.click|neswnsssn\.click|hdfdsi84j3h\.top|c85grt98sd\.com")
| stats count as hits, dc(src_ip) as internal_hosts, values(src_ip) as hosts by url
| sort - hits
The same domains in CrowdStrike, as DNS lookups from any host that fetched them. On a store network this also catches an administrator's browser hitting the injected storefront during testing — confirm the source before treating it as a victim machine.
#event_simpleName=DnsRequest
| DomainName=/lanhd6549tdhse\.top|deepveindrilling\.com|additionalver\.com|manulfie\.click|neswnsssn\.click|hdfdsi84j3h\.top|c85grt98sd\.com/
| table([@timestamp, ComputerName, UserName, DomainName])
| sort(@timestamp, order=desc)
Finally, hunt the second foothold. Sansec found a GSocket backdoor on a store that had every core patch, reached through a rogue admin account. This CrowdStrike query looks for GSocket tooling and for unexpected listeners on a store host — process names and listening sockets that have no business on a web server.
#event_simpleName=ProcessRollup2
| FileName=/^(gs-netcat|gsh|gsocket|gsocket_.*)$/ OR CommandLine=/gsocket|gs-netcat/i
| table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName])
| sort(@timestamp, order=desc)
Pair that with the admin-side check in Splunk: POSTs to the CMS block save route from sources that do not normally administer the store. The admin front name varies by installation (the default is admin), so match on the route, not the prefix.
index=web sourcetype=access_combined earliest=-30d http_method=POST uri_path="*/cms/block/save*"
| stats count as saves, dc(clientip) as sources, values(clientip) as ips, earliest(_time) as first_seen, latest(_time) as last_seen by uri_path
| sort - saves
Triage: sorting real compromise from noise
Start with the filesystem, because it settles the question faster than any log query. Sansec's own checks, run on the store host, are the fastest confirmation available:
find pub/media/custom_options/ pub/media/customer_address/ -type f ! -name '.htaccess'
find . -name 'accesson.php' -type f
Any .php, .phtml, or .phar file under the upload directories is a strong indicator of compromise — those folders should hold images and documents, never executable code. Sansec documented webshells disguised as PNG images in pub/media/custom_options/quote/, so also inspect image files there for PHP content; a file that opens as an image in a viewer but contains <?php is a polyglot shell.
Then check the database, where the loader lives. This is Sansec's recommended query against the CMS content — run it on cms_block and repeat it on cms_page:
SELECT block_id, identifier, update_time FROM cms_block
WHERE content LIKE '%fpCode%' OR content LIKE '%se_referrer%'
OR content LIKE '%localStorage.getItem%';
The update_time column gives you the attacker's most recent write. If the loader is present, note that deleting the script does not remove the attacker's access: the database write means they had a path in, and that path — the webshell, a rogue admin account, or a backdoor — is still there until you find it. Check admin_user for accounts you do not recognise, and look for unfamiliar processes on the host.
Separating signal from noise: a burst of guest-cart POSTs from a single IP can also be a load test, a scraper, or a misbehaving integration — confirm it against Hunt 2 before calling it exploitation. A .php request under /media/ that returns 404 is a probe, not a compromise; a 200 is. Loader-domain DNS hits from a corporate laptop are usually someone browsing an infected store, not your store being infected — the store-side proof is the cms_block query, not the DNS log. And a store can be patched and still compromised: the October loader appeared on a fully patched store through a rogue admin account, so a clean patch level never closes the investigation on its own.
Remediation and hardening
Patch first: install the July 2026 security release for your branch from APSB26-73 — the -2026-jul builds (for example 2.4.8-2026-jul), or Commerce Events 1.21.0 — and verify with php bin/magento --version afterwards. One version note: Adobe lists 2.4.9 as affected until the July patch, while Sansec reports the 2.4.9 general-availability release of May 12, 2026 already contained the fix; follow Adobe's list and install the July build regardless. Then hunt before you declare the incident closed: run the filesystem and database checks above, remove every webshell, backdoor, and injected script, and rotate the database, admin, and API integration passwords — Sansec's cleanup guidance is explicit that the passwords must change after the content is removed.
If you cannot patch today, two mitigations cut the risk. Block web access to the upload directory (pub/media/custom_options/) so uploaded PHP cannot execute — but note that this alone does not stop files being written. The control that stops the upload itself is a WAF rule filtering the guest-cart item endpoints, blocking requests that carry a file_info object. Sansec's advice is to do both, and to keep the WAF rule even after patching.
Two more October flaws deserve the same treatment while you are in the patching window: Adobe's October bulletins fixed StyleSmuggler (CVE-2026-75650, APSB26-146) and BlockStrap (CVE-2026-103849, APSB26-153), both critical Magento flaws. Neither was in the KEV catalog as of October 28, and neither is the subject of this hunt — but a store that missed the July patch has very likely missed the October ones too, so bring the release current rather than stopping at PolyShell. The same discipline applies to the earlier SessionReaper flaw (CVE-2025-54236): Sansec found 60 percent of infected stores carried files uploaded through PolyShell or SessionReaper, so check for both.
Sources
- NVD — CVE-2026-48356 (CVSS 9.3, CWE-434, affected versions)
- MITRE CVE record — CVE-2026-48356
- Adobe security bulletin APSB26-73 (affected and fixed versions, Adobe CVSS 9.6)
- CISA Known Exploited Vulnerabilities catalog (added October 27, 2026; due November 10, 2026)
- Sansec — Mass PolyShell attack wave hits 471 stores in one hour
- Sansec — deepveindrilling.com: Magento CMS block loader hits 953 stores
