← All posts

StyleSmuggler: what to do while there is no Magento patch

The first shop confirmed backdoored by StyleSmuggler was fully patched. The check to run today, a Cloudflare rule that stops the scanning, and the application guard that actually holds.

The first shop confirmed backdoored by StyleSmuggler was fully patched. July and August security updates applied, a clean patch-status report, and an attacker running their own code on it anyway.

So let us skip the part where you check your patch level and go straight to what you can actually do today.

What StyleSmuggler is

StyleSmuggler is an unauthenticated remote code execution flaw in Magento Open Source and Adobe Commerce, found and named by Sansec, exploited against live stores since 4 September. Unauthenticated means no account, no session, and no help from anyone inside your business.

Every current version is affected, including 2.4.9. As I write this there is no CVE, no Adobe advisory and no patch.

The attack smuggles PHP into Magento’s template system through a styles parameter, and gets it executed through the payment failed reminder email. It lands in var/report/, so it survives a cache flush.

Step 1. Check whether it already happened

From your Magento root on production:

grep -ril 'x_trace_' var/report/

Nothing back is good news. Something back is a conversation with your hosting party today, not next week. Sansec publish a fuller indicator list, including the process names and lock files the payload leaves behind. Check theirs before you rely on this one, because indicators move.

If you cannot run that yourself, that is fine. Ask the person who can.

Step 2. Block it at the edge

Most shops sit behind Cloudflare. This is a custom rule, action Block, and it takes about ten minutes.

(lower(http.request.uri.query) contains "styles[")
or (lower(http.request.uri.query) contains "styles%5b")
or (lower(http.request.uri.query) contains "<?")
or (lower(http.request.uri.query) contains "%3c%3f")
or (lower(http.request.uri.query) contains "generatorclass")
or (lower(http.request.uri.query) contains "with_resolved")
or (lower(http.request.uri.query) matches "([{][{]|%7b%7b)([ +]|%20)*(block|config|trans|var|depend)")
or (lower(http.user_agent) contains "<?")

Four things worth knowing before you paste that in.

  • Match styles[ with the bracket, never styles alone. Magento’s store locator ships a GraphQL query containing store_locator_advanced_custom_map_styles. Match the bare word and you block a query that runs on every page load. That one would have taken the site down, not protected it.

  • Put it at the top of your custom ruleset. If you already have skip rules for verified bots or for an office IP range, and they skip the current ruleset, then anything below them can be walked straight past.

  • The matches line needs a Business plan or higher. On Free or Pro, drop that line. The rest is plain contains and works everywhere.

  • Cover the User-Agent, not just the query string. One variant carries its payload there and aims it at a poisoned log line. That is the clause most of the published rules are missing.

Then check your own firewall event log for false positives before you walk away.

Step 3. Block it in the application

The edge rule stops opportunistic scanning, which is most of what is out there. It does not stop a payload in a POST body, and it does nothing at all if your origin server can be reached without going through Cloudflare. A surprising number can. That is worth finding out about your own shop regardless of this flaw.

So there is a second layer, and it is the one that actually holds. Three classes in Magento include or require a path handed to them by the caller:

setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php
setup/src/Magento/Setup/Module/Di/Code/Scanner/ArrayScanner.php
setup/src/Magento/Setup/Module/Di/Code/Scanner/XmlInterceptorScanner.php

They exist to serve bin/magento setup:di:compile and nothing else. They are never legitimately reached over HTTP. So refuse to run them over HTTP:

if (PHP_SAPI !== 'cli') {
    throw new \RuntimeException('Magento DI code scanners are CLI-only.');
}

That guard sits at the top of collectEntities, includeClass and _handleControllerClassName. It closes the sink rather than the entry point, so it holds whatever route the payload took. Disrex and proxiblue have both published this as a ready-made composer patch, which is the sane way to apply it, because composer install will otherwise overwrite a hand edit.

Apply it as a patch on magento/magento2-base and check that setup:di:compile still succeeds afterwards. Mine takes 44 seconds and passes.

The short version

Check var/report/. Put a rule at the edge today. Put the guard in the application this week. Take the guard back out when Adobe ships the real fix, and not a day before.

And if your answer to “are we exposed” was going to be “we are fully patched”, StyleSmuggler is the reason that is not an answer.

© 2026 Gunnie · Magento & Adobe Commerce Expert