No results found.

✕ Developing incident — no vendor patch (06/09/2026)

Adobe has published no advisory, CVE or workaround. Magento Open Source 2.4.7–2.4.9 is affected, with one confirmed victim running fully patched 2.4.6-p15. Treat Adobe Commerce and Cloud as affected until confirmed otherwise. Use this page as temporary containment until Adobe ships a fix.

StyleSmuggler Zero-Day

StyleSmuggler is an actively exploited, unauthenticated RCE affecting fully patched Magento Open Source and Adobe Commerce stores. The attack writes PHP into a Magento report or log file, then causes Magento to include it.

Start with the incident response overview. Research and live indicators are maintained in Sansec's advisory.

Check for compromise

Run as the Magento filesystem user, then repeat the process and socket checks as root.

crontab -l | grep -i gvfsd
grep -rl gvfsd /var/spool/cron/ 2>/dev/null
ls -la ~/.local/share/.gvfsd/ /tmp/.kw_* /tmp/.gvfsd[-_]* 2>/dev/null
ps -eo user,pid,ppid,comm,args | awk '$1 != "root" && $4 ~ /kworker/'

~ must resolve to the PHP-FPM user's home. The implant writes directly to the cron spool and uses both hyphenated and underscored paths. Remove its cron persistence before killing the process or it will recreate itself. A non-root kworker process is suspicious.

For each suspicious PID:

readlink -v /proc/<pid>/exe
readlink -v /proc/<pid>/cwd
sha256sum /proc/<pid>/exe
lsof -nP -a -p <pid>

Search reports and logs from the Magento root:

grep -Ral --fixed-strings '<?' var/report/ var/log/
grep -Ral -e 'X_TRACE_' -e 'X-TRACE-' var/report/ var/log/
grep -Fal 'array_merge()' var/log/system.log

The array_merge() error indicates Magento included a poisoned file. Disrex found successful infections using var/log/system.log, so search both var/report and var/log.

Run eComscan against the entire account. The implant lives above the document root.

~/bin/ecomscan --skip-dashboard --format=json --min-confidence=0 --deep /path/to/account/root

No matches or a clean scan do not prove the host is clean. Resolve scan errors before continuing.

⚠️ Collect evidence before you kill anything

Drain the node and restrict outbound traffic first. Preserve /proc/<pid>/exe, process metadata, sockets, cron and relevant logs. Do not reboot or run composer install: both can destroy useful evidence.

Contain StyleSmuggler

1. Update Sansec Shield

Sansec Shield has a live virtual patch. Update it, then hunt every node for compromises that pre-date the rules.

2. Block GraphQL if you don't use it

If you do not use GraphQL, block the complete route at every public ingress. Cover plain and index.php URLs, store prefixes and direct origin access. Stores using GraphQL must rely on Shield and application containment.

3. Temporary containment patch

These changes contain specific observed paths; they do not correct Magento's template system or rule out alternative gadgets and writable data sources. They do not clean an infected host.

Seven production changes. Save as stylesmuggler-hardening.patch:

diff --git a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php
index 4d2295f..a0dac3d 100644
--- a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php
+++ b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php
@@ -98,6 +98,15 @@ class PaymentFailuresService implements PaymentFailuresInterface
         string $message,
         string $checkoutType = 'onepage'
     ): PaymentFailuresInterface {
+        // Temporary StyleSmuggler containment: avoid the disclosed second-stage
+        // template rendering path. Revert once Adobe ships a supported fix.
+        $this->logger->warning(
+            'Payment failure email suppressed by temporary StyleSmuggler containment.'
+        );
+
+        return $this;
+
+        // phpcs:disable
         $this->inlineTranslation->suspend();
         $quote = $this->cartRepository->get($cartId);

diff --git a/lib/internal/Magento/Framework/Code/Generator/Io.php b/lib/internal/Magento/Framework/Code/Generator/Io.php
index a533ec8..3bbc788 100644
--- a/lib/internal/Magento/Framework/Code/Generator/Io.php
+++ b/lib/internal/Magento/Framework/Code/Generator/Io.php
@@ -153,11 +153,28 @@ class Io
      *
      * @param string $fileName
      * @return mixed
+     * @throws FileSystemException
      * @codeCoverageIgnore
      */
     public function includeFile($fileName)
     {
-        return include $fileName;
+        $generationDirectory = $this->filesystemDriver->getRealPath($this->_generationDirectory);
+        $realFileName = $this->filesystemDriver->getRealPath($fileName);
+        $generationDirectory = $generationDirectory
+            ? rtrim(str_replace('\\', '/', $generationDirectory), '/') . '/'
+            : false;
+        $realFileName = $realFileName ? str_replace('\\', '/', $realFileName) : false;
+
+        if (!$generationDirectory || !$realFileName || strpos($realFileName, $generationDirectory) !== 0) {
+            throw new FileSystemException(
+                new \Magento\Framework\Phrase(
+                    'The generated code file "%1" is outside the generation directory.',
+                    [$fileName]
+                )
+            );
+        }
+
+        return include $realFileName;
     }

     /**

diff --git a/lib/internal/Magento/Framework/Webapi/ErrorProcessor.php b/lib/internal/Magento/Framework/Webapi/ErrorProcessor.php
--- a/lib/internal/Magento/Framework/Webapi/ErrorProcessor.php
+++ b/lib/internal/Magento/Framework/Webapi/ErrorProcessor.php
@@ -18,7 +18,8 @@ use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Framework\Message\AbstractMessage;
 use Magento\Framework\Phrase;
 use Magento\Framework\Validator\Exception as ValidatorException;
 use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Serialize\Serializer\JsonHexTag;
 use Magento\Framework\Webapi\Exception as WebapiException;

 /**
@@ -98,7 +99,9 @@ class ErrorProcessor
         $this->_appState = $appState;
         $this->_logger = $logger;
         $this->_filesystem = $filesystem;
         $this->directoryWrite = $this->_filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
-        $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
+        // Web API report mitigation adapted from Graycore_StyleSmugglerPatch:
+        // https://github.com/graycoreio/magento2-style-smuggler-patch/commit/321b3d6a394f24b2ab4318068785889aeaa4ce16
+        $this->serializer = $serializer ?: ObjectManager::getInstance()->get(JsonHexTag::class);
         $this->registerShutdownFunction();
     }

diff --git a/pub/errors/processor.php b/pub/errors/processor.php
index 8cb386e..425c15b 100644
--- a/pub/errors/processor.php
+++ b/pub/errors/processor.php
@@ -8,7 +8,7 @@ declare(strict_types=1);
 namespace Magento\Framework\Error;

 use Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot;
-use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Serialize\Serializer\JsonHexTag;
 use Magento\Framework\Escaper;
 use Magento\Framework\App\ObjectManager;
 use Magento\Framework\App\Response\Http;
@@ -156,7 +156,7 @@ class Processor
     /**
      * JSON serializer
      *
-     * @var Json
+     * @var JsonHexTag
      */
     private $serializer;

@@ -172,20 +172,20 @@ class Processor

     /**
      * @param Http $response
-     * @param Json $serializer
+     * @param JsonHexTag $serializer
      * @param Escaper $escaper
      * @param DocumentRoot|null $documentRoot
      */
     public function __construct(
         Http $response,
-        ?Json $serializer = null,
+        ?JsonHexTag $serializer = null,
         ?Escaper $escaper = null,
         ?DocumentRoot $documentRoot = null
     ) {
         $this->_response = $response;
         $this->_errorDir  = __DIR__ . '/';
         $this->_reportDir = dirname(dirname($this->_errorDir)) . '/var/report/';
-        $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
+        $this->serializer = $serializer ?: ObjectManager::getInstance()->get(JsonHexTag::class);
         $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class);
         $this->documentRoot = $documentRoot ?? ObjectManager::getInstance()->get(DocumentRoot::class);
         if (!empty($_SERVER['SCRIPT_NAME'])) {
diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php
index b4aa770..015444e 100644
--- a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php
+++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php
@@ -132,6 +132,15 @@ class ClassesScanner implements ClassesScannerInterface
     private function includeClass(string $className, string $fileItemPath): bool
     {
         if (!class_exists($className)) {
+            // Temporary StyleSmuggler containment: this requires arbitrary PHP from disk,
+            // and Magento Setup is a command-line concern. Refuse to run under a web SAPI.
+            // phpcs:ignore Magento2.Security.Superglobal
+            if (PHP_SAPI !== 'cli' || isset($_SERVER['REQUEST_METHOD'])) {
+                throw new \RuntimeException(
+                    'Class files can only be scanned from a command-line process.'
+                );
+            }
+
             // phpcs:ignore
             require_once $fileItemPath;
             return true;

diff --git a/setup/src/Magento/Setup/Module/Di/Code/Scanner/ArrayScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Scanner/ArrayScanner.php
index 51ca31d..64a8007 100644
--- a/setup/src/Magento/Setup/Module/Di/Code/Scanner/ArrayScanner.php
+++ b/setup/src/Magento/Setup/Module/Di/Code/Scanner/ArrayScanner.php
@@ -15,6 +15,15 @@ class ArrayScanner implements ScannerInterface
      */
     public function collectEntities(array $files)
     {
+        // Temporary StyleSmuggler containment: DI array files include arbitrary PHP,
+        // and Magento Setup is a command-line concern. Refuse to run under a web SAPI.
+        // phpcs:ignore Magento2.Security.Superglobal
+        if (PHP_SAPI !== 'cli' || isset($_SERVER['REQUEST_METHOD'])) {
+            throw new \RuntimeException(
+                'DI array files can only be scanned from a command-line process.'
+            );
+        }
+
         $output = [];
         foreach ($files as $file) {
             if (file_exists($file)) {

diff --git a/setup/src/Magento/Setup/Module/Di/Code/Scanner/XmlInterceptorScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Scanner/XmlInterceptorScanner.php
index 880d77b..0a7261c 100644
--- a/setup/src/Magento/Setup/Module/Di/Code/Scanner/XmlInterceptorScanner.php
+++ b/setup/src/Magento/Setup/Module/Di/Code/Scanner/XmlInterceptorScanner.php
@@ -99,6 +99,15 @@ class XmlInterceptorScanner implements ScannerInterface
             $className = preg_replace('/^([0-9A-Za-z]*)_([0-9A-Za-z]*)/', '\\1_\\2_controllers', $className);
             $filePath = stream_resolve_include_path(str_replace('_', '/', $className) . '.php');
             if (file_exists($filePath)) {
+                // Temporary StyleSmuggler containment: this requires arbitrary PHP from disk,
+                // and Magento Setup is a command-line concern. Refuse to run under a web SAPI.
+                // phpcs:ignore Magento2.Security.Superglobal
+                if (PHP_SAPI !== 'cli' || isset($_SERVER['REQUEST_METHOD'])) {
+                    throw new \RuntimeException(
+                        'Controller class files can only be scanned from a command-line process.'
+                    );
+                }
+
                 require_once $filePath;
             }
         }
  • Report serializers use JsonHexTag, preventing new var/report and var/report/api files from containing raw PHP tags. The Web API change adapts Graycore's mitigation.
  • Io::includeFile() rejects files outside the generated-code directory.
  • DI scanners refuse to include PHP during web requests. CLI deployment commands still work.
  • PaymentFailuresService::handle() returns before rendering. Failed-payment emails remain disabled until the patch is removed.

For Composer projects, rebase the three app/code and lib/internal paths to their matching vendor/magento packages. pub/errors and setup come from magento2-base. Use your normal Composer patch tooling.

git apply --check stylesmuggler-hardening.patch
git apply stylesmuggler-hardening.patch

4. Quarantine files created before the patch

The patch does not change existing reports or protect var/log. Preserve suspicious files and move them outside every path available to the web runtime.

5. Server-level hardening

Consider disabling PHP's proc_open and mounting /tmp, /var/tmp and /dev/shm with noexec. Test first: some deployment tools and extensions shell out.

Deploy and verify

Deploy to drained nodes, rebuild DI metadata, recycle PHP-FPM and return nodes one at a time. Verify:

  • if blocked, GraphQL unreachable on every public hostname, and the origin blocked or authenticated
  • both the normal and Web API report writers default to JsonHexTag, and new reports escape tag characters
  • bin/magento setup:di:compile succeeds and the storefront renders after generated/ is cleared
  • failed-payment handling logs the containment warning without sending
  • no suspicious process, persistence file or report marker on any node

Remove containment only after an Adobe fix covering StyleSmuggler is deployed and verified on every node:

git apply -R stylesmuggler-hardening.patch

If a node is compromised

Assume the attacker read app/etc/env.php, the encryption key and every credential available to PHP-FPM. Do not disinfect in place.

  1. Preserve memory, disk and central logs.
  2. Rebuild from a known-good source revision and base image.
  3. Apply the mitigations above before accepting traffic.
  4. Rotate every secret readable by the compromised Unix account.
  5. Invalidate admin and customer sessions.
  6. Audit admin users, integrations, OAuth tokens, CMS content, email templates, database triggers, cron, queues and SSH keys.
  7. Hunt across every node, environment and shared home directory.

Checking the fleet at scale

Add the checks above to your healthcheck playbook and run them as the Magento filesystem user on every node. Treat scan errors as failures and roll out in waves so a hit can be investigated immediately.

Current IOCs

Last reviewed 06/09/2026. The campaign is live and infrastructure is rotating, so treat Sansec's advisory as the authoritative current source and re-check before every hunt.

From Sansec's StyleSmuggler research:

247.cdnflare.xyz
99.84.67.186:443
windwsecurity.run:443       # TCP, WebSocket over TLS
ntp.timesysnc.net:123       # UDP, NTP-shaped traffic
time.microsft.run:123       # UDP, NTP-shaped traffic
pool.microsft.studio:123    # UDP, NTP-shaped traffic
88.216.72.181               # attacker source
5.181.86.133                # attacker source

sha256 e315687a1dfe61ef4a5a5642214db6d3b2b05d81391285eebc2af664641a26a7

/tmp/.kw_<random><random>
~/.local/share/.gvfsd/gvfsd-user
~/.local/share/.gvfsd/.gvfsd_<8hex>.lock
process [kworker/u:8:0]
crontab */5 * * * * exec <home>/.local/share/.gvfsd/gvfsd-user

Disrex recovered two more, from disk and from the running process on the same host:

sha256 8334b434fa3fe9f59cebe9609b11e0b1fd19d10212c45c705adec1902a1d06ef   # on disk
sha256 251fabd50d7b18a8b5e1b3ef5d64e7198c17244778f6461fb1ab07f6169bf220   # in memory

Additional hash from a sample recovered during my own investigation:

sha256 b79dfdc1eed860e0b76c629d6adfce251db379b0b45a6d728d4ef483f7551420

Four hashes for one implant, two from the same machine. Hash matching is a bonus, not a detection strategy.

Do not rely on hashes or a single source IP. Look for the combination of process identity, executable path, persistence, outbound traffic, poisoned reports and the original web requests.

Sansec also lists requests involving /paypal/transparent/response/, /customer/section/load/ and /graphql with suspicious payloads. Correlate these with report/log content and process activity; the routes alone are legitimate traffic. Include UDP/123 in outbound hunting, not only HTTPS. Recheck the live advisory as indicators change.

References