⚠️ ACTIVE THREAT: Automated PolyShell attacks have hit 79.5% of all Magento stores globally. Mass exploitation is confirmed and ongoing. Do not wait — act today.
What Is the PolyShell Exploit?
The PolyShell exploit is a critical zero-day vulnerability (APSB25-94) affecting every production version of Magento 2 and Adobe Commerce. Discovered by security firm Sansec and disclosed on March 17, 2026, it allows a completely unauthenticated attacker — no login, no credentials, no admin access required — to upload executable PHP files directly to your server via Magento’s REST API.
The attack targets the pub/media/custom_options/ folder on your server. A successful exploit looks like this:
ls -l pub/media/custom_options/quote/3/2/
323index.php
The attacker then attempts to load that malicious file over the web:
GET /media/custom_options/quote/3/2/323index.php HTTP/2.0
If your server is misconfigured and allows PHP execution in that directory, the attacker now has full remote code execution on your store — meaning they can steal customer data, inject payment skimmers, install backdoors, and take complete control of your server.
Even if your server is correctly configured, the malicious file still lands on disk and remains there permanently — ready to activate the moment your configuration changes, your server migrates, or your web server is upgraded.
The good news? There are clear, actionable steps you can take right now to protect your store. Here are the three most important.
Step 1: Deny All Web Access to the
pub/media/custom_options/ Folder
The first and most critical line of defence is making sure that no file inside the pub/media/custom_options/ directory is ever publicly accessible via a browser — regardless of what gets uploaded there.
Choose the section below that matches your web server.
Option A: Nginx Configuration
Where to make the change: Your main Magento Nginx server block config — typically found at /etc/nginx/sites-available/yourdomain.conf or /etc/nginx/conf.d/magento.conf.
Step 1a — Block all access to the custom_options directory:
location /media/custom_options/ {
deny all;
}Step 1b — Block PHP execution across the entire media directory:
Add this block to ensure no .php file anywhere under /media/ can be executed, even if the deny all rule above is somehow bypassed:
location ~* ^/media/.*\.php$ {
deny all;
}Step 1c — Full recommended Nginx block (copy this into your server config):
# Block all direct access to custom_options uploads
location /media/custom_options/ {
deny all;
}
# Block PHP execution in the entire media directory
location ~* ^/media/.*\.(php|phtml|php3|php4|php5|php7|phar)$ {
deny all;
}
# Block PHP execution in pub/media as well (belt-and-braces)
location ~* ^/pub/media/.*\.(php|phtml|php3|php4|php5|php7|phar)$ {
deny all;
}
⚠️ Critical Nginx gotcha: The deny all rule for /media/custom_options/ can be silently overridden by a \.php$ location block that appears later in your config and passes PHP requests to FastCGI. Always audit your full Nginx config — not just the block you add. Order matters in Nginx. After editing, reload Nginx:
nginx -t && systemctl reload nginx
Option B: Apache Configuration
Apache uses .htaccess files placed directly inside directories to control access. Magento ships with pre-configured .htaccess files in the right locations — but these can be accidentally deleted, overwritten, or bypassed.
Step 1a — Verify/create pub/media/custom_options/.htaccess:
This file should deny all web access to anything inside custom_options/. Check if it exists:
cat pub/media/custom_options/.htaccess
It should contain:
<IfVersion < 2.4>
order deny,allow
deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
If the file is missing or different, recreate it:
cat > pub/media/custom_options/.htaccess << 'EOF'
<IfVersion < 2.4>
order deny,allow
deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
EOFStep 1b — Verify pub/media/.htaccess disables PHP execution:
This parent .htaccess should prevent PHP from running anywhere inside the pub/media/ directory:
cat pub/media/.htaccess
It must contain:
php_flag engine 0
If this line is missing, add it:
echo “php_flag engine 0” >> pub/media/.htaccess
Step 1c — Full recommended pub/media/custom_options/.htaccess (complete version):
# Deny all web access to files in this directory
<IfVersion < 2.4>
order deny,allow
deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
# Block PHP execution as additional protection
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phar)$">
<IfVersion < 2.4>
order deny,allow
deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
</FilesMatch>
⚠️ Critical Apache gotcha: .htaccess protection only works if AllowOverride is set to All (or at minimum Limit) in your Apache VirtualHost block. If your hosting provider has set AllowOverride None, .htaccess files are completely ignored. Check your Apache VirtualHost:
After making changes, restart Apache:
<Directory /var/www/html/pub/media>
AllowOverride All
</Directory>apachectl configtest && systemctl reload apache2
# or on CentOS/RHEL:
systemctl reload httpd
How to Test Both Servers
After applying either configuration, verify your protection is working correctly:
Test 1 — Access denied test:
# Create a harmless test file
touch pub/media/custom_options/test.php
# Try to access it from the web
curl -o /dev/null -s -w "%{http_code}" https://www.yourdomain.com/media/custom_options/test.php
You must receive 403. Any other response means the configuration is not working.
Test 2 — PHP execution test:
# Create a simple PHP test file
echo "<?php echo 'VULNERABLE'; ?>" > pub/media/custom_options/exectest.php
# Try to execute it
curl https://www.yourdomain.com/media/custom_options/exectest.phpYou must receive a 403 Forbidden response — not the word VULNERABLE. If you see VULNERABLE, PHP is executing in this directory and your server is at critical risk.
Clean up after testing:
rm pub/media/custom_options/test.php pub/media/custom_options/exectest.phpStep 2: Make the custom_options Folder Read-Only
Even if web access is blocked, uploads can still succeed at the application layer unless the directory itself is locked down. The simplest way to prevent any malicious file from landing on disk in the first place is to make the pub/media/custom_options folder read-only at the filesystem level.
First, empty the folder of any legitimate uploaded files (or move them to a safe backup location):
# Confirm the folder is empty or back up existing contents first
ls -la pub/media/custom_options/
Then remove write permissions:
chmod 444 pub/media/custom_options With the folder set to read-only, even if an attacker successfully sends a crafted upload request to Magento’s REST API, the server will be unable to write the malicious file to disk. The exploit is blocked at the filesystem level — a clean, reliable defence that requires no application-level changes.
Note: If your store uses custom product options of type “file” for legitimate customer uploads, you will need to assess whether restricting write access impacts that functionality. In most cases, stores that do not actively use file-type product options can apply this safely.
Step 3: Put Your Magento Codebase Under Git Version Control
The first two steps harden your server against the exploit. This step ensures you are immediately alerted if anything slips through — or if an attacker has already gained a foothold before you applied the mitigations.
Place your Magento codebase, including the pub/media directory, under Git version control and check for unexpected file changes on a regular schedule:
git status pub/media/custom_options
Go further by writing a simple Bash script that runs git status across your entire Magento installation and sends you an email or Slack notification if any new or modified files are detected:
#!/bin/bash
CHANGES=$(git -C /var/www/html status --porcelain)
if [ -n "$CHANGES" ]; then
echo "$CHANGES" | mail -s "⚠️ Magento File Change Alert" [email protected]
fiSet this script to run as a cron job twice a day at minimum:
# Run at 8am and 8pm daily
0 8,20 * * * /path/to/magento-monitor.sh
This approach gives you a near-real-time early warning system. If a new PHP file appears in pub/media/custom_options/ — or anywhere else on the server — you will know about it within hours, not weeks. Speed of detection is the difference between a manageable incident and a full store compromise.
Bonus: Apply the Community Patch
In addition to the three steps above, we strongly recommend applying the community-developed PolyShell patch while waiting for Adobe to release an official production fix.
The patch, available at github.com/markshust/magento-polyshell-patch, enforces an image-only file extension allowlist (jpg, jpeg, gif, png) at the application layer — blocking the malicious upload before it ever reaches the filesystem.
Install it via Composer:
composer require markshust/magento2-module-polyshell-patch
bin/magento module:enable MarkShust_PolyshellPatch
bin/magento setup:upgrade
bin/magento cache:flush
This patch is lightweight, designed not to conflict with Adobe’s eventual official fix, and is the fastest way to close the application-layer vulnerability directly.
Summary: Your 3-Step PolyShell Protection Checklist
| Step | Action | Priority |
|---|---|---|
| 1 | Deny all web access to pub/media/custom_options/ via Nginx or Apache config | 🔴 Immediate |
| 2 | Set pub/media/custom_options to read-only with chmod 444 | 🔴 Immediate |
| 2 | Place codebase under Git and run scheduled file change monitoring | 🟠 Today |
| Bonus | Apply community patch via Composer | 🔴 Immediate |
PolyShell is widely exploited right now. You can protect your store by denying public access to the upload folder, making it read-only so malicious files cannot be written to disk, and monitoring for any unexpected file changes with Git.
Need Help Securing Your Magento 2 Store?
At Aims Infosoft, our certified Magento developers have been securing stores for over 10 years. We can apply all three protection steps, audit your server configuration, scan for existing compromise indicators, and set up ongoing monitoring — so you can focus on running your business, not fighting attackers.
👉 Contact Aims Infosoft for a Free Security Quote →
Aims Infosoft — #1 Magento Solutions Provider | 10+ Years | 90% Client Satisfaction | 52+ Products | 12+ Extensions