PolyShell: Critical File Upload Vulnerability in the Magento 2 REST API

On March 17, 2026, the Sansec forensics team published details on a critical vulnerability dubbed PolyShell. It affects all versions of Magento Open Source and Adobe Commerce, and allows an unauthenticated attacker to upload an executable file through the REST API. No patch is available for production versions. Let’s look at what this actually means and how to respond.


What PolyShell exploits

The name refers to the attack technique: a polyglot file, designed to pass as an image while containing executable code. A disguised webshell.

The vulnerability targets the REST flow for custom options of type file on cart products. When such an option exists, Magento processes a file_info object containing base64-encoded data, a MIME type and a filename. The file is then written to pub/media/custom_options/quote/, with no proper validation of its actual content or extension.

Two key points:

  • GraphQL mutations use a different code path and are not affected.
  • The vulnerable code has been present since the very first Magento 2 release.

Not all stores are exposed the same way

The unauthenticated upload affects everyone. However, severity then depends on the server configuration.

Stored XSS (JavaScript execution in an admin’s browser): versions prior to 2.3.5, or server configurations that publicly expose the upload directory.

RCE (remote code execution): stock nginx configurations from versions 2.0.0 to 2.2.x via an index.php filename, any nginx configuration passing .php files to FastCGI without restriction, and Apache configurations prior to 2.3.5 without php_flag engine 0.

In short, two stores with the same application bug can end up at very different risk levels. The issue isn’t just in the code. It sits at the intersection of the code, the server configuration and the hosting provider’s choices.


What Sansec says, what Adobe says

This is the point that requires the most care, because there is a real tension between the two readings.

Sansec’s position is clear. The fix only exists in version 2.4.9-alpha3, which is a pre-release. No isolated patch, no backport to production branches. Sansec had not observed active exploitation at the time of publication, but notes that the exploit method is already circulating.

Adobe’s side: bulletin APSB25-94 (October 2025) does list affected and fixed versions on production branches: 2.4.8-p3, 2.4.7-p8, 2.4.6-p13, 2.4.5-p15, 2.4.4-p16. But this bulletin covers multiple CVEs. The question is whether the specific PolyShell fix is part of what was backported.

Sansec writes explicitly: “Patched — 2.4.9-alpha3+ (pre-release only)”. If the fix had been included in the October 2025 security releases, there would be no reason for them to publish an urgent warning in March 2026 stating there is no patch for production.

So don’t confuse “APSB25-94 includes patches for production branches” (true, for other CVEs) with “the PolyShell fix is in those patches” (contradicted by Sansec). This distinction changes everything for anyone deciding what to do right now.

As of March 23, 2026: the latest security releases are 2.4.8-p4, 2.4.7-p9, 2.4.6-p14 and 2.4.5-p16 (bulletin APSB26-05, released March 10). Upgrade if you haven’t already: they address other critical vulnerabilities. But don’t assume that’s enough for PolyShell.


What to do now

With no application-level fix available for production, the action plan rests on three pillars: harden, verify, clean up.

Test your exposure

A simple curl will tell you if the directory is publicly accessible:

bash

curl -I https://your-store.com/media/custom_options/

You should get a 403 Forbidden. If you get a 200 or a directory listing, you need to act immediately.

Block access to the directory

Blocking public access doesn’t prevent uploads (the file still lands on disk via the API), but it prevents direct access to the uploaded content. This is a mitigation, not a fix.

For nginx, make sure a restriction block exists and isn’t overridden by a broader regex rule:

nginx

location /media/custom_options {
    deny all;
    return 403;
}

Watch out: a location ~ \.php$ rule positioned earlier can match first. This is a classic pitfall that can completely bypass your protection.

For Apache, don’t just check that a .htaccess file “exists”. If your AllowOverride is set to None at the vhost level, it does nothing. Same goes if a custom configuration takes precedence higher up.

Check what’s already on disk

Don’t just look for .php files. Look for any suspicious extension and any recently created files:

bash

find pub/media/custom_options/ -type f \( -name "*.php" -o -name "*.phtml" -o -name "*.pht" \)

bash

find pub/media/custom_options/ -type f -mtime -30

Sansec notes that eComscan was updated with PolyShell-specific detection patterns on March 17. That’s an option for a more thorough scan.

The trap you shouldn’t forget

Even if the uploaded file isn’t executable today, it stays on disk. A server migration, a vhost change, an overly permissive nginx config, and what seemed contained suddenly becomes exploitable. That’s why hardening alone isn’t enough. You need to harden, verify, then clean up.


A useful reminder

If your store is running on an unsupported version (below 2.4.6 for Magento Open Source, below 2.4.4 for Adobe Commerce with extended support), the question is no longer “how to work around PolyShell”. The question is how long you keep stacking risk on a codebase that’s no longer maintained. PolyShell is a wake-up call, but on a store that’s three years behind on patches, it’s far from the only problem.