# CVE-2026-75650 StyleSmuggler Magento RCE

Source: https://www.egnworks.com/blog/style-smuggler-turns-magento-templates-into-rce  
Author: Jacob Strix  
Published: 2026-09-10  
Updated: 2026-09-10  
Category: Security  
Tags: CVE-2026-75650, Adobe Commerce, Magento, RCE, Template Injection, Incident Response

> A technical analysis of CVE-2026-75650, the actively exploited Magento template injection that gives unauthenticated attackers remote code execution.

---

A storefront field that should describe presentation can become server-side PHP execution. StyleSmuggler, tracked as CVE-2026-75650, crosses that boundary inside Adobe Commerce and Magento Open Source without requiring an account, an administrator session, or interaction from a victim. Adobe assigned the flaw a CVSS 3.1 score of 10.0 and released the emergency VULN-39341 hotfix on September 7, 2026. By then, attackers had already been compromising stores for three days.

The vulnerability is not interesting because its score is perfect. It is interesting because the exploit joins several behaviors that look ordinary in isolation. Magento accepts structured storefront data. Error handling stores attacker-influenced text in a report. The email template system later resolves objects and processes style-related properties. A reachable include operation turns the resulting path into code. The dangerous transition is not a single obvious `eval()` call. It is a data-flow chain across components that disagree about whether the value is inert text, template input, or a file to execute.

The incident also demonstrates why patch status is not the same as host integrity. The first confirmed victim was current on the July and August 2026 security updates. Successful attacks installed Linux implants, cron persistence, disguised processes, and PHP web shells. Applying the hotfix prevents the same entry path, but it cannot remove a payload already running or invalidate credentials already read from the store.

## The Facts That Change Priority

Adobe's APSB26-146 advisory covers Adobe Commerce, Adobe Commerce B2B, and Magento Open Source. The affected range includes every supported Magento 2.4 branch in the advisory, with the August 2026 releases and earlier listed as vulnerable. Adobe rates the update Priority 1, confirms exploitation in the wild, and requires a version-matched hotfix rather than waiting for a routine release.

| Field | Verified detail |
| --- | --- |
| CVE | `CVE-2026-75650` |
| Name used by researchers | StyleSmuggler |
| Published | September 7, 2026 |
| Weakness | CWE-1336, improper neutralization in a template engine |
| Impact | Arbitrary code execution |
| CVSS 3.1 | `10.0` |
| Vector | `AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H` |
| Authentication | None |
| Exploitation | Confirmed in the wild |
| CISA status | Known Exploited Vulnerabilities catalog |
| Adobe fix | `VULN-39341` hotfix |
| Supported affected branches | Adobe Commerce 2.4.4 through 2.4.9, Magento Open Source 2.4.4 through 2.4.9, and listed B2B branches |

The scope change in the CVSS vector matters. A request handled as storefront input crosses into the authority of the PHP application process. On a typical deployment, that process can read Magento configuration, database credentials, encryption material, integration tokens, and files writable by the web service account. It may also reach the database, cache, message queue, payment integrations, and internal services trusted by the application.

This is why an e-commerce RCE has a wider consequence than defacing a catalog page. The server holds secrets that connect the storefront to systems outside Magento. Adobe's remediation guidance therefore requires merchants to rotate the encryption key and every credential it protected, including administration passwords, REST, SOAP, and GraphQL integration tokens, OAuth secrets, payment gateway credentials, database passwords, SSH or deployment keys, and third-party extension API keys.

## Where the Trust Boundary Breaks

StyleSmuggler is a two-stage chain. The first stage places PHP-bearing data somewhere Magento will later treat as input. Sansec observed attackers using generated failure reports, although file-backed custom options provide another storage route. The second stage causes the failed-payment email path to process the poisoned value through Magento's template system. A `styles` property helps the data reach an include-capable component while evading safeguards that were expected to keep template variables inert.

```mermaid
flowchart TD
    A["Anonymous storefront request"] --> B["Poisoned data is stored"]
    B --> C["Payment failure triggers email"]
    C --> D["Template resolves styles data"]
    D --> E["File include executes PHP"]
    E --> F["Web shell or Linux implant"]
```

Calling this merely a malicious email template misses the vulnerability. The attacker does not need access to the administration panel to edit a template. Instead, attacker-controlled runtime data is carried into an existing template execution path. The template engine becomes the confused component: it receives a graph of objects and properties from code that assumes the data is safe, then follows behaviors that are too powerful for anonymous input.

The chain is also not limited to one session backend. Moving sessions from files to Redis or the database does not remove the vulnerable semantics. Sansec recorded an operator whose session-storage attempt failed and who retried eight seconds later through a custom-option upload path. The storage object changed. The transition from stored attacker data to template execution remained.

That difference is operationally important. A WAF signature for one request body may block a known payload while leaving another carrier reachable. The vendor hotfix changes the application boundary. Edge rules can buy time, but they are not equivalent to the patch.

## Read the Patch as a Set of Controls

The VULN-39341 patch should be evaluated as a control set rather than as one changed line. A safe review asks which data types the template path now rejects, where dangerous tags are neutralized, which object traversal is stopped, and whether the deployed tree contains every expected change for its exact version.

This matters because Adobe published several version-matched archives. Applying a patch built for a neighboring release can fail partially, apply with offsets, or produce a deployment that looks changed without carrying the complete control set. The reliable workflow starts by identifying the exact Composer package versions, selecting Adobe's matching patch, deploying it through the normal release process, and verifying both patch status and application behavior.

For Adobe Commerce on Cloud, Adobe documents a Quality Patches Tool check that searches for VULN-39341 and confirms an `Applied` state:

```bash
vendor/bin/magento-patches -n status | grep '39341\|Status'
```

That output is evidence about the deployment mechanism, not proof that the server was never exploited. It should be recorded alongside the package lock, patch checksum, deployment timestamp, and results from a clean staging test. Operators running on-premises installations need an equivalent check against the actual release artifact used by PHP-FPM, not only the source directory in a CI workspace.

The September isolated security patch does not automatically include the StyleSmuggler hotfix. Adobe explicitly instructs merchants to apply the CVE-2026-75650 hotfix in addition to the regular September update. A scanner that reports only the monthly patch level can therefore produce a dangerous false negative.

## A Public PoC With an Honest Boundary

A public Docker validation lab reproduces the component-level PHP execution primitive on a revision-pinned Magento Open Source 2.4.9 checkout. It then applies Adobe's patch and sends the identical input through the same component. The unpatched state writes a random marker to `/tmp`; the patched state must not. This is a proper A/B test because the observable side effect is independent of an HTTP status or an exception message.

The lab is deliberately non-destructive. Its fixed payload cannot run an operator-supplied command, open a callback, download a file, or create a shell. Only the developer storefront is exposed, and it binds to loopback. MariaDB, Redis, OpenSearch, PHP-FPM, and the report gateway remain isolated inside Docker.

```bash
git clone https://github.com/dinosn/cve-2026-75650-magento-validation-lab.git
cd cve-2026-75650-magento-validation-lab
cp .env.example .env
make up
make wait
make ab
```

The expected result separates report storage from component execution:

```text
Unpatched report:    raw-tag=true,  guard=false
Patched report:      raw-tag=false, guard=true, neutralized=true
Unpatched component: marker=true
Patched component:   marker=false
[PASS] Report storage and component execution match the required A/B controls.
```

There is an essential limitation. The public lab does not prove the stock unauthenticated HTTP connector from the external request to the template model. It starts at Magento's real email-template component and proves the dangerous sink plus the patch boundary. Sansec reproduced the full remote chain on clean 2.4.7, 2.4.8, and 2.4.9 installations, but withheld the assembled request. Describing the public repository as a full weaponized exploit would be false.

That limitation does not weaken the defensive value. The lab answers two useful questions without publishing the live attack chain: whether the component executes the marker on a pinned vulnerable build, and whether the version-matched Adobe fix blocks the identical input. It is suitable for regression testing in an isolated environment and for understanding why a superficial HTTP-only check is insufficient.

## Validate a Deployment Without Executing the Bug

The same lab includes a read-only validator for an owner-controlled Magento tree. It does not start Magento or execute code from the mounted installation. The container runs without network access, without Linux capabilities, with a read-only root filesystem, and with the target mounted read-only.

```bash
make validate TARGET=/srv/magento
```

A fully matched result reports nine controls present and returns `FULL_CONTROL_SET_PRESENT`. Anything less should be treated as an unconfirmed patch state, not automatic proof of exploitability. Edition, release, custom patches, and backports can all change file shape. Confirm the installed package versions and deploy Adobe's supported hotfix before drawing a security conclusion.

For fleet-scale triage, the following Python program inventories Composer metadata and likely VULN-39341 artifacts without modifying the installation. It produces JSON suitable for ingestion into a SIEM or asset pipeline.

```python
#!/usr/bin/env python3
import argparse
import hashlib
import json
from pathlib import Path


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for block in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(block)
    return digest.hexdigest()


def load_json(path: Path):
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (OSError, UnicodeDecodeError, json.JSONDecodeError):
        return None


def package_versions(root: Path) -> dict[str, str]:
    lock = load_json(root / "composer.lock")
    if not isinstance(lock, dict):
        return {}
    wanted = {
        "magento/product-community-edition",
        "magento/product-enterprise-edition",
        "magento/magento2-base",
    }
    result = {}
    for section in ("packages", "packages-dev"):
        for package in lock.get(section, []):
            name = package.get("name")
            version = package.get("version")
            if name in wanted and isinstance(version, str):
                result[name] = version
    return result


def patch_artifacts(root: Path) -> list[dict[str, object]]:
    matches = []
    for path in root.rglob("*"):
        if not path.is_file() or "39341" not in path.name.lower():
            continue
        try:
            relative = path.relative_to(root).as_posix()
            matches.append(
                {
                    "path": relative,
                    "size": path.stat().st_size,
                    "sha256": sha256(path),
                }
            )
        except OSError:
            continue
    return matches


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("root", type=Path)
    args = parser.parse_args()
    root = args.root.resolve()
    report = {
        "root": str(root),
        "composer_packages": package_versions(root),
        "vuln_39341_artifacts": patch_artifacts(root),
        "classification": "inventory_only",
    }
    print(json.dumps(report, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
```

Run it with the web root or release root and preserve its output with the change record:

```bash
python3 inventory_vuln_39341.py /srv/magento > vuln-39341-inventory.json
python3 -m json.tool vuln-39341-inventory.json >/dev/null
```

The script intentionally reports inventory rather than `vulnerable` or `safe`. A patch archive sitting on disk does not prove it was applied. A missing archive does not prove the code lacks the fix because a deployment may vendor patched packages or bake changes into an immutable artifact.

## Detect the Request Before the Payload Runs

Sansec observed exploitation through storefront requests carrying `styles` input. The exact encoding and carrier changed as operators iterated, so detection should normalize request bodies and parameter names before matching. Preserve raw evidence separately because decoding can destroy distinctions needed during investigation.

The following Sigma-style rule is a starting point for normalized reverse-proxy or WAF logs. Field names must be mapped to the local schema.

```yaml
title: Magento Style Parameter Exploitation Pattern
id: 3c8ecf18-53eb-4d0e-8e03-756502026910
status: experimental
description: Detects anonymous Magento requests carrying PHP or template syntax in style-related input
author: Egnworks
date: 2026-09-10
logsource:
  category: webserver
detection:
  magento_route:
    url.path|contains:
      - '/graphql'
      - '/rest/'
      - '/checkout/'
  style_input:
    http.request.body.content|contains:
      - 'styles['
      - 'styles%5B'
      - '"styles"'
  executable_content:
    http.request.body.content|contains:
      - '<?php'
      - '%3C%3Fphp'
      - '{{'
      - 'include'
  condition: magento_route and style_input and executable_content
falsepositives:
  - Authorized security validation against a staging store
level: critical
tags:
  - attack.initial-access
  - attack.t1190
```

This rule is intentionally narrower than a blanket match on `styles`. Magento storefronts legitimately submit presentation and product-option data. A high-confidence alert needs the route, the style-related parameter, and executable or template syntax together. Separate lower-severity analytics can count unusual anonymous `styles` submissions, sudden error-report creation, and repeated failed-payment email events from one source.

Detection should also correlate application behavior. A request that creates a report, followed within seconds by email-template processing and a new PHP-FPM child writing an executable or starting an unexpected process, is stronger than any string match. The useful telemetry spans the edge, Magento logs, PHP process execution, file integrity, cron, and network egress.

## Hunt the Post-Exploitation State

The first reported campaign did not stop at proving code execution. It launched a small Rust backdoor and changed its disguise over several days. Observed names included a fake kernel worker, `fc-cache`, and `chronyd`. Some builds installed cron persistence. Another could relaunch without a cron entry. The implant used UDP port 123 and domains resembling time services, but its traffic did not behave like a legitimate NTP client.

| Signal | Why it matters |
| --- | --- |
| Userspace binary claiming `[kworker/u:8:0]` | Real bracketed kernel workers do not map to a normal userspace executable |
| `fc-cache` outside the system package path | The campaign copied its implant below a user cache directory |
| `chronyd` running from `/tmp/.chrony-*` | Legitimate chronyd should run from a managed system path |
| Nine 48-byte UDP packets in a burst | The implant mimicked NTP while sending a repeated chunk sequence |
| NTP server-mode packets emitted by a client | A normal client has no reason to identify every outbound packet as a server reply |
| Cron entries referencing `.gvfsd`, `.fc-*`, or `.chrony-*` | Observed persistence paths use hidden user and temporary directories |
| PHP files beneath product image cache | A second actor installed a token-gated web shell there |
| `x_trace_` strings in `var/report` | Report artifacts can retain evidence from the poisoning stage |

Process provenance defeats several disguises. Compare `/proc/<pid>/exe`, package ownership, command-line arguments, parent process, cgroup, namespace, and socket ownership. A process named `chronyd` that descends from PHP-FPM, executes from `/tmp`, and sends server-mode UDP bursts is not normal time synchronization even when the destination port is 123.

File inspection must include Magento's report directory, media tree, generated content, cache directories, temporary paths, user crontabs, the cron spool, and service-account home directories. Do not rely only on `crontab -l`. Sansec observed persistence written directly into a spool file, which can omit the normal syslog event produced by the `crontab` command.

## Treat Patching and Recovery as Separate Tracks

Containment should preserve evidence before it changes the host. Isolate the storefront at the load balancer, snapshot disks where policy permits, retain volatile process and socket data, export relevant cloud and network logs, and record the exact application artifact. If revenue requirements prevent full isolation, route traffic to a known-clean deployment and keep the suspected nodes out of rotation.

The recovery sequence is strict because several actions destroy evidence or create false confidence.

| Order | Action | Required outcome |
| ---: | --- | --- |
| 1 | Preserve | Process, filesystem, application, WAF, DNS, flow, and cloud evidence captured |
| 2 | Contain | Suspected nodes removed from customer traffic and outbound access restricted |
| 3 | Scope | Initial request, created files, processes, persistence, accounts, and accessed secrets identified |
| 4 | Rebuild | Clean image or release artifact deployed with the matching VULN-39341 hotfix |
| 5 | Rotate | Magento encryption key and every dependent credential replaced at its source |
| 6 | Validate | A/B staging test, patch-control verification, and negative production telemetry confirmed |
| 7 | Monitor | Fleet-wide IOC and behavioral hunts remain active after restoration |

Credential rotation must occur after the vulnerable path is closed and from a trusted administrative environment. Rotating secrets while an implant remains active gives the attacker the replacements. Rotating only Magento's encryption key is also incomplete because an attacker may already have copied plaintext credentials, tokens, or database contents.

Rebuilding is preferable when code execution is confirmed, the original entry time is uncertain, root access may have been obtained, or more than one actor touched the server. File-by-file cleanup assumes every artifact has been found. The observed campaign already included changing payloads, multiple process names, cron and non-cron persistence, and a second actor deploying a separate web shell. That assumption is too weak for an internet-facing payment application.

## What This Bug Teaches Beyond Magento

StyleSmuggler is a parser-boundary failure expressed through an application framework. One subsystem stores text. Another maps properties. A template engine resolves objects. A lower layer interprets a path as executable PHP. Each step can appear defensible when reviewed alone. The vulnerability exists in the composition.

Security tests therefore need to follow attacker-controlled values across asynchronous and secondary workflows. An input accepted during checkout may not become dangerous until a failure report is written, a queue consumer runs, or an email template renders. Testing only the immediate HTTP response misses the execution point.

Patch verification needs the same end-to-end discipline. A green package scanner, an uploaded patch archive, or a successful Composer command is not enough. The strongest evidence combines exact version inventory, presence of every patch control, a non-destructive regression test on a matching staging build, and production telemetry showing that the old behavior no longer occurs.

Finally, a maximum CVSS score is only the beginning of prioritization. StyleSmuggler deserves emergency handling because exploitability, exposure, and consequence line up: the vulnerable surface is internet facing, authentication is unnecessary, active attacks preceded the fix, affected software holds high-value credentials, and successful exploitation leaves a persistent host compromise that patching alone cannot reverse.

## References

[Adobe Security Bulletin APSB26-146](https://helpx.adobe.com/security/products/magento/apsb26-146.html)

[Adobe Commerce urgent action for CVE-2026-75650](https://experienceleague.adobe.com/en/docs/commerce-knowledge-base/kb/announcements/commerce-apsb26-146)

[Sansec StyleSmuggler technical investigation](https://sansec.io/research/stylesmuggler-0day)

[CISA Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)

[CVE-2026-75650 Docker validation lab](https://github.com/dinosn/cve-2026-75650-magento-validation-lab)

[StyleSmuggler mitigation and incident-response notes](https://github.com/disrex-group/stylesmuggler-mitigation)
