← Back to Feed CACHED · 2026-10-28 07:09:22 · CACHE_KEY CVE-2026-48356
CVE-2026-48356 · CWE-434 · Disclosed 2026-07-14

Adobe Commerce and Magento Open Source guest cart REST API unrestricted file upload (PolyShell)

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone left the loading dock unlocked and the burglars have been backing up trucks for seven months

CVE-2026-48356, publicly tracked as PolyShell, is an unrestricted file upload in Adobe Commerce, Magento Open Source, Adobe Commerce B2B, and Adobe I/O Events for Commerce. The flaw lives in the REST API's guest cart custom-options handler (/V1/guest-carts/:cartId/items). An unauthenticated attacker supplies a base64-encoded polyglot file — a valid image header stapled to a PHP payload — along with a .php extension. The only server-side check, getimagesizefromstring(), validates the image header and ignores the filename extension entirely. The file is written to pub/media/custom_options/quote/. On web servers that pass .php requests in the media directory to the PHP engine (a common misconfiguration on both nginx and Apache), this yields unauthenticated remote code execution. On servers that block PHP in media but still serve static files, the result is stored XSS that fires when any user — including admins — views the upload. Affected products: Adobe Commerce ≤2.4.9, ≤2.4.8-p5, ≤2.4.7-p10, ≤2.4.6-p15, ≤2.4.5-p17, ≤2.4.4-p18; Magento Open Source ≤2.4.9, ≤2.4.8-p5, ≤2.4.7-p10, ≤2.4.6-p15; Commerce B2B ≤1.5.3; and Adobe I/O Events ≤1.20.0. Fixes shipped in Adobe bulletin APSB26-73 on July 14, 2026, as the -2026-jul builds across all supported lines, and Events 1.21.0.

Adobe rates this CRITICAL at 9.3 with the CVSS vector AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N. The UI:R component reflects the stored-XSS / account-takeover interpretation — a victim must visit the page. However, the documented RCE attack path requires zero user interaction: the attacker uploads, then directly requests the webshell. Sansec reported mass exploitation beginning March 16, 2026 — 471 stores compromised in a single hour on March 30 — and Akamai confirmed automated scanning from March 19. CISA added it to the KEV catalog on October 27, 2026, with a federal remediation deadline of November 10. The vendor's 9.3 is defensible for the XSS path, but under-scores the RCE path that has been weaponized at industrial scale for seven months. We upgrade to 9.5 CRITICAL to reflect the proven, unauthenticated, mass-exploited RCE chain.

"Unauthenticated webshell upload actively mass-exploited since March; patch or block immediately"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Obtain guest cart ID and product SKU

The attacker creates a guest cart via POST /V1/guest-carts (returns a cart ID) and enumerates product SKUs via the public catalog or GraphQL. Both are default-enabled, unauthenticated Magento REST endpoints. No credentials, no cookies, no CAPTCHA. This is indistinguishable from normal storefront API traffic.
Conditions required:
  • Target runs Adobe Commerce or Magento Open Source with REST API enabled (default)
  • Store has at least one product with a custom-option of type 'file' — OR the validator skips option-type checking on guest routes (the bug)
Where this breaks in practice:
  • None — REST API is enabled on every default Magento install
Detection/coverage: Baseline REST API logging; volume anomaly detection on /V1/guest-carts creation
STEP 02

Upload polyglot PHP file via guest cart custom options

The attacker sends POST /V1/guest-carts/:cartId/items with a JSON body containing a custom_options array. The file_info object holds base64-encoded data (a GIF89a or PNG header prepended to <?php payload), a MIME type like image/gif, and a filename ending in .php. The ImageContentValidator calls getimagesizefromstring(), sees a valid image header, and passes. No extension check, no option-type validation on guest routes. The file is written to pub/media/custom_options/quote/<hash>/filename.php. Weaponized tooling: abraxas/CVE-2026-48356 (Python, validated by VulnCheck) and piecar21/CVE-2026-48356.
Conditions required:
  • Valid guest cart ID and product SKU from step 1
  • Attacker can craft a polyglot file (trivial — public tools generate these)
Where this breaks in practice:
  • None — the upload is a single unauthenticated HTTP POST; no rate limiting, no CAPTCHA, no CSRF token required
Detection/coverage: WAF signatures: Akamai Adaptive Security Engine rule 3000982 (deployed Mar 27, 2026); ModSecurity rules from rfxn.com; file-extension monitoring on pub/media/custom_options/
STEP 03

Trigger PHP execution on the uploaded file

The attacker requests the uploaded file directly: GET /media/custom_options/quote/<hash>/filename.php. If the web server's configuration passes .php requests in the media directory to PHP-FPM, the polyglot executes. On nginx, this happens when a broad ~ \.php$ location block precedes the media deny rules, or when the nginx.conf.sample deny rules were never applied. On Apache, it happens when .htaccess in pub/media is missing or AllowOverride is disabled. The result is unauthenticated remote code execution as the web server user.
Conditions required:
  • Web server passes PHP requests in pub/media/ to the PHP engine
  • The uploaded file is web-accessible (default — pub/media is the document root for static assets)
Where this breaks in practice:
  • Magento's shipped nginx.conf.sample includes a deny rule for PHP in media directories — properly configured installs block execution
  • Apache installs with correct .htaccess and AllowOverride All also block PHP in media
  • However, mass exploitation of 471 stores in one hour proves a large fraction of production installs are misconfigured
Detection/coverage: Access log entries for .php requests under /media/custom_options/; PHP-FPM process spawning from media directory paths
STEP 04

Deploy webshell and establish persistence

With code execution, the attacker drops a full webshell (observed: accesson.php across multiple directories, including pub/media/customer_address/). The webshell provides interactive command execution, file browsing, and database access. Attackers create rogue admin users, install malicious API integrations, and inject JavaScript loaders (fpCode, se_referrer patterns) into cms_block and cms_page tables for Magecart-style card skimming.
Conditions required:
  • Successful RCE from step 3
  • Web server user has write access to Magento directories and database credentials in app/etc/env.php
Where this breaks in practice:
  • Minimal — the web server user (www-data/nginx/apache) typically has write access to the entire Magento directory tree and read access to database credentials in env.php
Detection/coverage: File integrity monitoring (FIM) on web root; CMS content scanning for fpCode/se_referrer/localStorage.getItem patterns; admin user audit; API integration review
STEP 05

Data exfiltration and supply-chain JavaScript injection

The attacker injects a JavaScript loader into CMS blocks that executes on every storefront page, capturing payment card numbers, login credentials, and PII in real time (Magecart attack). Observed campaign infrastructure includes rotating loader domains. The attacker also exfiltrates the database (customer records, order history, hashed passwords) and env.php (database credentials, encryption keys, API keys). This constitutes a PCI DSS breach and a supply-chain attack against every customer of the compromised store.
Conditions required:
  • Persistent webshell access from step 4
  • Store processes payment data or holds customer PII (true for virtually all production Commerce instances)
Where this breaks in practice:
  • Content Security Policy (CSP) headers can block injected external scripts — but most Magento stores have permissive or no CSP
  • Subresource Integrity (SRI) on checkout scripts would detect tampering — rarely implemented on CMS-injected blocks
Detection/coverage: CSP violation reports; external domain monitoring in JavaScript; PCI ASV scans detecting injected scripts; customer complaints about suspicious charges
03 · Compensating Control

1
CRITICAL 9.5→HIGH 7.5
SEVERITY REDUCED
Block PHP execution in pub/media/ immediately — Add a location directive in nginx that returns 403 for any .php, .phtml, or .phar request under /media/ — place it before any ~ \.php$ block. For Apache, ensure pub/media/.htaccess contains php_flag engine off and RemoveHandler .php .phtml .phar, and verify AllowOverride All is active. This breaks the RCE chain at step 3 entirely. Deploy within the noisgate mitigation SLA of 3 days (CRITICAL) — but given active exploitation, deploy within hours.
2
CRITICAL 9.5→MEDIUM 5.5
SEVERITY REDUCED
Deploy WAF rule blocking dangerous extensions on guest cart routes — If running Akamai App & API Protector, enable Adaptive Security Engine rule 3000982 (deployed Mar 27, 2026). For ModSecurity, apply rfxn.com's published rules targeting .php/.phtml/.phar extensions in file_info payloads on /V1/guest-carts/ endpoints. This blocks the upload at step 2. Deploy within hours given KEV status.
3
CRITICAL 9.5→LOW 3.0
SEVERITY REDUCED
Deny all web access to pub/media/custom_options/ — Add a location /media/custom_options/ { deny all; return 403; } rule in nginx or equivalent Deny from all in Apache .htaccess for the custom_options directory. This prevents both RCE execution and XSS rendering of uploaded files, neutralizing the upload even if the WAF is bypassed. This matches the directive in Magento's shipped nginx.conf.sample.
4
CRITICAL 9.5→CRITICAL 9.5
Scan for IOCs and compromised CMS content — Search pub/media/custom_options/ for any .php, .phtml, or .phar files and for non-image content (polyglots). Search for accesson.php across the entire web root. Query cms_block and cms_page database tables for fpCode, se_referrer, localStorage.getItem, and unfamiliar <script> tags. Audit admin users, API integrations, and recent password changes. If IOCs are found, treat as a confirmed breach: rotate all credentials (database, admin, API keys, encryption key in env.php), engage incident response, and notify per PCI DSS requirements. Run this scan immediately — do not wait for patching.
5
CRITICAL 9.5→IGNORE 0.0
SEVERITY REDUCED
Apply APSB26-73 patch to all Commerce instances — Install the -2026-jul security builds for your specific product line per APSB26-73. Upgrade Adobe I/O Events to 1.21.0. Test in staging first (checkout, file-option products, headless REST cart flows), then deploy to all production nodes. This is the only complete remediation. Target the noisgate remediation SLA of 90 days — but with KEV listing and active exploitation, treat this as emergency patching and deploy within days, not weeks.
What doesn't work
  • Rate limiting on cart endpoints — the exploit is a single HTTP POST per upload; rate limiting at typical thresholds (e.g., 100 req/min) will not trigger before the payload lands.
  • Upgrading PHP runtime version — the vulnerability is in Magento's PHP application code (ImageContentValidator), not in the PHP interpreter. PHP 8.x is equally vulnerable.
  • Content Security Policy headers alone — CSP can block the *post-exploitation* JavaScript injection (Magecart phase) but does nothing to prevent the upload or webshell execution. It is a useful defense-in-depth layer but not a compensating control for this CVE.
  • Disabling guest checkout in the storefront UI — the REST API endpoint /V1/guest-carts may remain accessible even when guest checkout is disabled in the admin panel, depending on Magento version and configuration. Do not rely on this without verifying the API route is actually blocked.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationActive since March 16, 2026. Sansec reported first probing Mar 16, automated mass scanning by Mar 19, and a mass exploitation wave on Mar 30 that compromised 471 stores in one hour. A new campaign wave with fresh loader domains was reported Oct 24, 2026. CISA SSVC assessment: *exploitation active, technical impact total*.
CISA KEVListed October 27, 2026. Federal remediation deadline: November 10, 2026. Known ransomware campaign use: Unknown. Australia's ACSC also issued an advisory citing a *substantial number* of vulnerable instances.
Proof-of-conceptTwo public PoCs on GitHub: abraxas/CVE-2026-48356 (Python, 7 files, validated by VulnCheck as initial-access exploit) and piecar21/CVE-2026-48356. The abraxas PoC targets localhost lab environments and includes Docker Compose files for reproduction. Also indexed on Sploitus and Vulners.
EPSS30.58% probability of exploitation in next 30 days — 98.2nd percentile. This places it in the top 2% of all scored CVEs.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N → 9.3 Critical. The UI:R reflects the stored-XSS path (victim visits page). The documented RCE path requires no user interaction. S:C (scope changed) is accurate — the web server context differs from the Magento application context. PR:N confirms unauthenticated access.
Affected versionsAdobe Commerce ≤2.4.9, ≤2.4.8-p5, ≤2.4.7-p10, ≤2.4.6-p15, ≤2.4.5-p17, ≤2.4.4-p18; Magento Open Source ≤2.4.9, ≤2.4.8-p5, ≤2.4.7-p10, ≤2.4.6-p15; Commerce B2B ≤1.5.3, ≤1.5.2-p5, ≤1.4.2-p10, ≤1.3.4-p17, ≤1.3.3-p18; Adobe I/O Events ≤1.20.0. Vulnerable code reportedly dates to first Magento 2 release.
Fixed versionsAPSB26-73 -2026-jul builds across all supported lines (2.4.4 through 2.4.9). Adobe I/O Events fixed in 1.21.0. Note: these are *not* standard Composer version strings — follow APSB26-73 installation instructions. The partial fix first appeared in 2.4.9-alpha3 (APSB25-94) but production backports were not available until July 14, 2026.
Scanning & exposure100,000+ stores estimated worldwide running Magento/Adobe Commerce (scandiweb estimate). All are internet-facing by design. Akamai deployed WAF rule 3000982 on Mar 27. No GreyNoise or Shodan tag confirmed, but the attack surface is the entire public Magento install base.
Disclosure timelineMar 17, 2026: Sansec publicly names flaw 'PolyShell' (APSB25-94 reference). Mar 27: Akamai WAF rule deployed. Jul 14, 2026: CVE-2026-48356 published, APSB26-73 production patches released. Oct 24: New campaign wave reported. Oct 27: CISA KEV listing.
Researcher / orgDiscovered and named by Sansec (Willem de Groot). Analysis and WAF coverage by Akamai. PoC by Abraxas Labs (abraxas). ModSecurity rules by rfxn.com. Related advisory coverage by ThaiCERT, ACSC, and HKCERT.

Sources.

  1. Adobe Security Bulletin APSB26-73
  2. Safeguard.sh — CVE-2026-48356 KEV Analysis and IOCs
  3. Akamai — Magento PolyShell Threat Analysis (APSB25-94)
  4. BleepingComputer — PolyShell Unauthenticated RCE on Magento
  5. The Hacker News — Magento PolyShell Mass Exploitation
  6. Sploitus — abraxas PoC for CVE-2026-48356
  7. Strix.ai — CVE-2026-48356 Intelligence
  8. CVE-Security.com — CVE-2026-48356 Detail
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.5/10)

Why this verdict

  • Proven mass exploitation removes theoretical friction. The CVSS vector's UI:R assumes user interaction, but the RCE path — upload polyglot, request webshell — requires none. Sansec documented 471 stores compromised in one hour on March 30, 2026, proving the full chain is automated and works at scale. This has been actively exploited for over seven months.
  • Zero authentication, zero friction on the upload step. The guest cart REST API is enabled by default on every Magento install. The attacker needs only a cart ID (one POST) and a product SKU (public catalog). The image validator is trivially bypassed with a polyglot header. There is no CAPTCHA, rate limit, or CSRF token on this endpoint.
  • Server configuration is the only gate to RCE — and it fails at scale. The nginx.conf.sample deny rule for PHP in media directories is the intended mitigation, but the mass exploitation wave proves a large fraction of production installs either never applied it, have a broad ~ \.php$ block that takes precedence, or run Apache without proper .htaccess enforcement.
  • EPSS and KEV confirm operational severity. EPSS at 30.58% (98.2nd percentile) places this in the top 2% of all CVEs. CISA KEV listing with a 14-day federal deadline and ACSC advisory underscore real-world urgency.
  • Role multiplier: Adobe Commerce and Magento are e-commerce platforms — they are internet-facing production systems by definition, processing payment card data (PCI DSS scope) and storing customer PII. A compromised store yields: (a) supply-chain-scale blast radius via Magecart JavaScript injection hitting every customer session, (b) mass data exfiltration of cardholder data, order history, and credentials, (c) persistent backdoor access to the underlying server via webshell. This is not a workstation bug — 100% of the installed base occupies the high-value e-commerce tier. The floor is CRITICAL.

Why not higher?

The reassessed score of 9.5 already reflects the worst realistic outcome. A 10.0 would require the RCE path to succeed on every installation without any configuration dependency. In practice, properly configured nginx installs using the shipped nginx.conf.sample deny PHP execution in media directories, which blocks the RCE chain entirely and leaves only stored XSS. The server-config prerequisite is a real friction point — it just fails often enough to sustain mass exploitation.

Why not lower?

Downgrading below CRITICAL is not defensible. The attack is unauthenticated, internet-facing, actively mass-exploited at industrial scale for seven months, has multiple public PoCs, is KEV-listed, and targets a platform that is canonically a high-value-role component (PCI-scoped e-commerce with supply-chain blast radius via JavaScript injection). Even if we only counted the stored XSS path (which genuinely requires UI), the combination of unauthenticated upload + admin session hijack + PCI data exposure would floor this at HIGH. The proven RCE path pushes it firmly into CRITICAL territory.

06 · Verification

Crowdsourced verification payload.

Run this script on each Magento/Adobe Commerce application server as a user with read access to the Magento installation directory. Invoke it as: bash check_cve_2026_48356.sh /var/www/magento (replace with your Magento root path). No root privileges required — just filesystem read access. The script checks the installed version, inspects the code for the patch, evaluates server configuration, and scans for known IOCs.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-48356 (PolyShell) Verification Script
# Outputs: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 1=VULNERABLE, 0=PATCHED, 3=UNKNOWN
set -uo pipefail

MAGENTO_ROOT="${1:-}"
if [[ -z "$MAGENTO_ROOT" ]]; then
  echo "Usage: $0 /path/to/magento/root"
  echo "UNKNOWN - No Magento root directory specified"
  exit 3
fi
if [[ ! -d "$MAGENTO_ROOT" ]]; then
  echo "UNKNOWN - Directory not found: $MAGENTO_ROOT"
  exit 3
fi
if [[ ! -f "$MAGENTO_ROOT/composer.json" ]]; then
  echo "UNKNOWN - Not a Magento installation (no composer.json)"
  exit 3
fi

echo "=== CVE-2026-48356 (PolyShell) Verification ==="
echo "Magento root: $MAGENTO_ROOT"

# --- Detect version from composer.lock ---
VERSION=""
if [[ -f "$MAGENTO_ROOT/composer.lock" ]]; then
  VERSION=$(python3 -I -c "
import json, sys
with open(sys.argv[1]) as f:
    data = json.load(f)
for pkg in data.get('packages', []):
    if pkg['name'] in ('magento/product-community-edition', 'magento/product-enterprise-edition'):
        print(pkg['version'])
        break
" "$MAGENTO_ROOT/composer.lock" 2>/dev/null || true)
fi
echo "Detected version: ${VERSION:-UNKNOWN}"

VULNERABLE=0
PATCHED=0

# --- Check version string for July 2026+ patch ---
if [[ -n "$VERSION" ]]; then
  if echo "$VERSION" | grep -qiE '2026-(jul|aug|sep|oct|nov|dec)'; then
    echo "[PATCH] Version string contains July 2026+ security patch identifier"
    PATCHED=1
  else
    echo "[INFO] Version $VERSION does not contain 2026-jul+ patch identifier"
  fi
fi

# --- Check for code-level fix in ValidatorFile ---
VALIDATOR="$MAGENTO_ROOT/vendor/magento/module-catalog/Model/Product/Option/Type/File/ValidatorFile.php"
if [[ -f "$VALIDATOR" ]]; then
  if grep -qE 'getAllowedExtensions|validateFileExtension|blockDangerousExtension|extensionBlacklist' "$VALIDATOR" 2>/dev/null; then
    echo "[PATCH] ValidatorFile.php contains file extension validation (patched)"
    PATCHED=1
  else
    echo "[VULN] ValidatorFile.php lacks file extension validation (vulnerable)"
    VULNERABLE=1
  fi
else
  echo "[INFO] ValidatorFile.php not found at expected path; check manually"
fi

# --- Check ImageContentValidator for extension enforcement ---
IMG_VALIDATOR=$(find "$MAGENTO_ROOT/vendor/magento" -path '*/ImageContentValidator.php' -type f 2>/dev/null | head -1)
if [[ -n "$IMG_VALIDATOR" ]]; then
  if grep -qE 'extension|dangerousType|blockPhp' "$IMG_VALIDATOR" 2>/dev/null; then
    echo "[PATCH] ImageContentValidator contains extension checks"
    PATCHED=1
  else
    echo "[VULN] ImageContentValidator lacks extension enforcement"
    VULNERABLE=1
  fi
fi

# --- Server config checks ---
echo ""
echo "=== Server Configuration (RCE mitigation) ==="
MEDIA_DIR="$MAGENTO_ROOT/pub/media"
if [[ -f "$MEDIA_DIR/.htaccess" ]]; then
  if grep -qiE 'php_flag engine off|RemoveHandler.*\.php|SetHandler.*None|deny from all' "$MEDIA_DIR/.htaccess" 2>/dev/null; then
    echo "[OK] .htaccess in pub/media blocks PHP execution"
  else
    echo "[WARN] .htaccess exists but may not block PHP execution"
  fi
else
  echo "[INFO] No .htaccess in pub/media (check nginx config manually)"
fi

# --- IOC scan ---
echo ""
echo "=== IOC Scan ==="
IOC_FOUND=0
CUSTOM_DIR="$MEDIA_DIR/custom_options"
if [[ -d "$CUSTOM_DIR" ]]; then
  PHP_IN_OPTS=$(find "$CUSTOM_DIR" -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.phar' \) 2>/dev/null | head -20)
  if [[ -n "$PHP_IN_OPTS" ]]; then
    echo "[ALERT] PHP files in custom_options/:"
    echo "$PHP_IN_OPTS"
    IOC_FOUND=1
  fi
  POLYGLOT=$(find "$CUSTOM_DIR" -type f -exec grep -rl '<?php' {} + 2>/dev/null | head -10)
  if [[ -n "$POLYGLOT" ]]; then
    echo "[ALERT] Files containing PHP code in custom_options/:"
    echo "$POLYGLOT"
    IOC_FOUND=1
  fi
fi
BACKDOOR=$(find "$MAGENTO_ROOT/pub" -name 'accesson.php' -type f 2>/dev/null | head -5)
if [[ -n "$BACKDOOR" ]]; then
  echo "[ALERT] Known backdoor accesson.php found:"
  echo "$BACKDOOR"
  IOC_FOUND=1
fi
if [[ $IOC_FOUND -eq 0 ]]; then
  echo "[OK] No file-based IOCs found (also check cms_block/cms_page DB tables)"
fi

# --- Verdict ---
echo ""
echo "=== VERDICT ==="
if [[ $PATCHED -eq 1 && $VULNERABLE -eq 0 ]]; then
  echo "PATCHED"
  exit 0
elif [[ $VULNERABLE -eq 1 ]]; then
  echo "VULNERABLE"
  [[ $IOC_FOUND -eq 1 ]] && echo "WARNING: IOCs detected — possible active compromise"
  exit 1
else
  echo "UNKNOWN - Could not definitively determine patch status. Review manually."
  exit 3
fi