Adobe Commerce / Magento CVE-2026-75650 (StyleSmuggler) - テンプレート署名の判定ミスが招いた未認証RCEと、3日間のゼロデイ悪用

Adobe Commerce / Magento CVE-2026-75650 (StyleSmuggler) - テンプレート署名の判定ミスが招いた未認証RCEと、3日間のゼロデイ悪用

作成日:
読了:64
更新日:
この記事を読む人におすすめPR / Amazonアソシエイト

当サイトは Amazon.co.jp を宣伝しリンクすることで紹介料を得る手段を提供する、Amazonアソシエイト・プログラムの参加者です。価格・在庫はリンク先の最新情報をご確認ください。

ECサイトの基盤は、攻撃者から見れば「決済情報と顧客情報が集まっていて、しかもインターネットに必ず面している」サーバーです。Adobe Commerce / Magento Open Sourceはその代表格で、2022年のTrojanOrder、2024年のCosmicSting、2025年のSessionReaperと、ほぼ毎年のように大規模な悪用が起きてきました。

2026年9月、そこに4本目が加わりました。CVE-2026-75650、Sansecが「StyleSmuggler」と名付けた未認証のリモートコード実行です。CVSS v3.1は満点の10.0。原因はMagentoのテンプレートエンジンにある「この指示子は親テンプレートに委ねられたものか」を判定する1つの比較式が、「単に解決できなかった指示子」まで同じ扱いにしてしまっていたことにあります。攻撃者が顧客データとして送り込んだテンプレート構文が署名され、あとから権限の強いフィルタで実行される。そこにエラーレポートファイルの汚染を組み合わせることで、PHPの実行に至ります。

しかも今回はゼロデイでした。Sansecが最初の悪用を確認したのが2026年9月4日22:20 UTC、Adobeがout-of-bandのセキュリティ速報APSB26-146とホットフィックスVULN-39341を公開したのが9月7日20:20 UTC。3日間、パッチが存在しない状態で店舗が侵害され続けました。CISAは9月8日にKEVへ追加し、是正期限を9月11日という3日後に設定しています。

この記事では、Adobeのセキュリティ速報、repo.magento.comから取得したホットフィックスの実diff、magento/magento2のソースコード、NVD、CISA KEVのJSONフィード、Sansecの観測レポートという一次情報をもとに、何が壊れていたのか、攻撃者が何を仕込んだのか、そして運用側が今日何をすべきかを整理します。

WARNING

Adobeの速報が影響対象として挙げているのは、Adobe Commerce 2.4.4から2.4.9の各系統(2026-aug以前)、Adobe Commerce B2B 1.3.3から1.5.3の各系統(2026-aug以前)、Magento Open Source 2.4.6から2.4.9の各系統(2026-aug以前)です。NVDのCPEはさらに広く、2.4.4より前の全バージョンも脆弱としています。サポート終了済みの2.2系・2.3系・2.4.0から2.4.3に対してAdobeは修正を出していません。修正は完全なリリースではなくホットフィックス(composerパッチ)として提供されており、「最新のパッチリリースを当てているから安全」とは言えません。Sansecによれば最初の被害店舗は2.4.6-p15に2026年7月・8月のパッチを適用済みでした。

NOTE

本記事の数値・バージョン・日付は、Adobeセキュリティ速報APSB26-146(2026年9月7日公開)、Adobe Experience Leagueのナレッジベース記事、repo.magento.comから取得したVULN-39341-composer-patches.zip、magento/magento2の2.4-developブランチ、NVD(2026年9月9日最終更新)、CISA KEVカタログのJSONフィード(カタログバージョン2026.09.10、収録件数1705)、Sansecの調査記事(2026年9月9日12:00 UTC最終更新)を2026年9月11日時点で確認したものです。攻撃の完全なガジェットチェーンはSansecが「続報で公開する」としており未公開のため、本文中の機構説明には推定を含む箇所があります。それらは明示し、裏付けの無い項目は「一次情報で確認できなかった事項」に分けて記載しています。

概要(まず結論)

項目内容
CVECVE-2026-75650
通称StyleSmuggler(Sansec命名)
セキュリティ速報APSB26-146(2026年9月7日公開、Priority Rating 1)
対象製品Adobe Commerce、Adobe Commerce B2B、Magento Open Source
脆弱性の種類CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine
影響任意コード実行(Arbitrary code execution)
認証の要否不要(Adobe速報の"Authentication required to exploit?"はNo)
CVSS基本値(v3.1)10.0 CRITICAL(スコア提供元はAdobe PSIRT、NVD上の種別はSecondary)
CVSSベクタ(v3.1)CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
CVSS v4.0未付与
影響バージョン2.4.4から2.4.9の全系統(2026-aug以前)。NVDでは2.4.4未満も対象
修正ホットフィックスVULN-39341(composerパッチ)
悪用の有無Adobeが「exploited in the wild」を明記。最初の確認は2026年9月4日22:20 UTC(Sansec)
NVD公開2026年9月7日21:17 UTC(最終更新2026年9月9日)
CISA KEV2026年9月8日追加、是正期限2026年9月11日、forensicTriage: Yes
ランサムウェア利用KEVの記録上はUnknown
SSVC(CISA)exploitation: active、automatable: yes、technicalImpact: total

CVSSベクタのS:C(Scope: Changed)が満点の決め手です。テンプレートエンジンという「HTMLを組み立てるコンポーネント」の脆弱性が、PHPプロセスそのもの、さらにOS上のバックドア設置にまで及ぶ。脆弱なコンポーネントの権限境界を越えているので9.8ではなく10.0になっています。同じAdobeのCVSS 10.0事例としては、7月のColdFusion APSB26-68がありましたが、あちらはAdobeが「悪用は未確認」としていました。今回は速報時点で悪用が確認されている点が決定的に違います。

Magentoのテンプレートエンジンとは何か

先に前提を押さえておきます。Magentoのメールテンプレート、CMSページ、ウィジェットは、二重波括弧で囲んだ「ディレクティブ」を埋め込める独自のテンプレート言語で書かれています。代表的なものを挙げます。

ディレクティブ役割
{{var customer}}変数の展開
{{trans "Reason"}}翻訳
{{depend shippingMethod}} ... {{/depend}}条件付き出力
{{block class="..." template="..."}}ブロッククラスを生成してHTMLを描画
{{template config_path="..."}}別テンプレートの埋め込み
{{inlinecss file="..."}}CSSファイルをインライン化
{{css file="..."}}CSSファイルの読み込み

このうち{{block}}は、指定したクラス名のオブジェクトをDIコンテナ経由で生成し、そのメソッドを呼ぶという非常に強いディレクティブです。CMSページやメールテンプレートに管理者が書くことを想定した機能で、これが顧客の入力から呼べてしまえば、それだけで危険です。だからMagentoは、顧客由来のデータ(名前、住所など)がテンプレートに流れ込んでも{{block}}が実行されないよう、複数の防御層を持っています。今回の脆弱性は、その防御層のうち「署名」の仕組みが抱えていた設計上の穴です。

処理系は3層の継承になっています。

Magento\Widget\Model\Template\Filter
  -> Magento\Cms\Model\Template\Filter
    -> Magento\Email\Model\Template\Filter   (blockDirective などを実装)
      -> Magento\Framework\Filter\Template   (基底。var / trans / depend / if / for / template を実装)

基底のFramework\Filter\TemplateにはblockDirectiveがありません。{{block}}を解釈できるのはEmail\Model\Template\Filter以上の層だけです。この非対称性が、後述する穴の核心になります。

影響を受けるバージョンと修正

Adobe速報の影響バージョン表をそのまま示します。

製品影響を受けるバージョン
Adobe Commerce2.4.9-2026-aug以前、2.4.8-2026-aug以前、2.4.7-2026-aug以前、2.4.6-2026-aug以前、2.4.5-2026-aug以前、2.4.4-2026-aug以前
Adobe Commerce B2B1.5.3-2026-aug以前、1.5.2-2026-aug以前、1.4.2-2026-aug以前、1.3.4-2026-aug以前、1.3.3-2026-aug以前
Magento Open Source2.4.9-2026-aug以前、2.4.8-2026-aug以前、2.4.7-2026-aug以前、2.4.6-2026-aug以前

表記の「2026-aug」は、Adobeが2026年8月に出したセキュリティパッチリリースを指します。つまり2026年8月時点の最新パッチを当てていても脆弱です。

Magento Open Sourceの行が2.4.6から始まっている点は補足が要ります。Adobe Experience Leagueのナレッジベース記事では「Magento Open Source: 2.4.4 through 2.4.9-2026-aug and earlier」とされており、ホットフィックスのzipにも2.4.4-p18用と2.4.5-p17用のパッチが同梱されています。NVDのCPE設定は81エントリで、cpe:2.3:a:adobe:commerce:*versionEndExcluding: 2.4.4(2.4.4より前はすべて脆弱)を含み、2.4.4から2.4.9までの各パッチレベルとアルファ・ベータ版を列挙しています。Sansecも「2.4.4から2.4.9まですべて影響」としたうえで、クリーンな2.4.7・2.4.8・2.4.9で未認証チェーン全体を再現したと報告しています。実務上は2.4系のすべてが対象と考えるべきです。

修正はホットフィックスのみ

修正版のリリースは(執筆時点で)なく、VULN-39341-composer-patches.zipというcomposerパッチだけが提供されています。zipの中身はバージョン別の6ファイルです。

パッチファイル対象
VULN-39341_244-p18.patch2.4.4系
VULN-39341_245-p17.patch2.4.5系
VULN-39341_246-p15.patch2.4.6系
VULN-39341_247-p10.patch2.4.7系
VULN-39341_248-p5.patch2.4.8系
VULN-39341_249.patch2.4.9系

Experience Leagueの記事によれば、これに加えて古いパッチレベル向けの個別zip(VULN-39341_248-p3-patch.zipVULN-39341_248-p1.patch.zipVULN-39341_247-p8.patch.zipVULN-39341_247-p5.patch.zipVULN-39341_246-p13.patch.zipVULN-39341_246-p11.patch.zip)も用意されています。Adobeが動作検証したのは各系統の2026-augリリースに対してで、Sansecは「それより古いパッチレベルにも脆弱性はあるが、パッチの検証はされていない」と注意しています。

Adobeが何も出していない2.2系・2.3系・2.4.0から2.4.3-p3については、Scandiwebが同じ9ファイルの変更を41バージョンにバックポートしたと公表しています(2026年9月8日)。Sansecはこのバックポートを「レビューしていないので、ステージングで先に試すこと」と書いています。本記事でも内容は未検証です。

タイムライン

日時(UTC)出来事
2026年9月4日22:20StyleSmugglerの悪用を初めて確認(Sansec)
2026年9月4日23:10SansecのeComscanが、無関係な複数店舗でインプラントを検知
2026年9月5日Sansecがクリーンな2.4.7・2.4.8・2.4.9でチェーンを再現
2026年9月5日07:15Sansec Shieldがブロック開始
2026年9月5日Sansecが調査記事を公開(パッチ未提供のまま)
2026年9月6日インプラントがfc-cacheに改名、バージョン2.1.4
2026年9月7日別グループがPHP Webシェルを設置しているのを確認
2026年9月7日17:30同グループが2.4.7-p10の店舗でpub/mediaの書き込み可否を探索
2026年9月7日インプラントがchronydに改名、バージョン2.1.5
2026年9月7日20:20AdobeがAPSB26-146とホットフィックスVULN-39341を公開
2026年9月7日21:17NVDがCVE-2026-75650を公開
2026年9月8日CISAがKEVへ追加(是正期限9月11日)。ScandiwebがEOL版41本へバックポート
2026年9月9日NVDレコード最終更新。Adobeナレッジベース記事最終更新

「最初の悪用から公式修正まで3日」という数字は、そのまま「その3日間に踏まれた店舗は、パッチを当てても既に侵害済み」を意味します。Sansecが「パッチは穴を塞ぐが、すでに侵入された店舗を掃除はしない」と強調しているのはそのためです。

技術的な中身:「遅延ディレクティブ」の署名判定

テンプレートフィルタの二段階処理

Magento\Framework\Filter\Template::filter()は、テンプレート文字列を2回走査します。2.4-developブランチの該当箇所を引用します。

// lib/internal/Magento/Framework/Filter/Template.php
public function filter($value)
{
    // ...
    $this->filteringDepthMeter->descend();
 
    // Processing of template directives.
    $templateDirectivesResults = array_unique(
        $this->processDirectives($value),
        SORT_REGULAR
    );
    $value = $this->applyDirectivesResults($value, $templateDirectivesResults);
 
    // Processing of deferred directives received from child templates
    // or nested directives.
    $deferredDirectivesResults = array_unique(
        $this->processDirectives($value, true),
        SORT_REGULAR
    );
    $value = $this->applyDirectivesResults($value, $deferredDirectivesResults);
 
    if ($this->filteringDepthMeter->showMark() > 1) {
        // Signing own deferred directives (if any).
        $signature = $this->signatureProvider->get();
 
        foreach ($templateDirectivesResults as $result) {
            if ($result['directive'] === $result['output']) {
                $value = str_replace(
                    $result['output'],
                    $signature . $result['output'] . $signature,
                    $value
                );
            }
        }
    }
    // ...
}

1回目の走査は通常のディレクティブ処理です。2回目は$isSigned = trueで走り、署名付きのディレクティブだけを処理します。

なぜ「署名」という仕組みが必要なのか。{{inlinecss}}が理由です。CSSのインライン化はHTML全体が揃ってからでないと行えないので、子テンプレート(たとえばメールのヘッダー部品)の中に{{inlinecss}}があった場合、子は処理せずにそのまま親へ返します。Email\Model\Template\Filter::inlinecssDirective()の該当部分です。

// app/code/Magento/Email/Model/Template/Filter.php
public function inlinecssDirective($construction)
{
    // ...
    // If this template is a child of another template, skip processing so that the parent template will process
    // this directive. This is important as CSS inlining must operate on the entire HTML document.
    if ($this->isChildTemplate()) {
        return $construction[0];
    }
    // ...
}

親は、子から上がってきたHTMLの中にある{{inlinecss}}を実行したい。しかし親は同時に、顧客名や住所のような変数展開の結果として現れたディレクティブ風の文字列は実行してはいけない。この2つを区別するために、子は「自分が意図的に親へ委ねたディレクティブ」にリクエスト単位のランダムな署名を付け、親は署名付きのものだけを2回目の走査で処理する、という設計になっています。SignatureProviderクラスのdocコメントには「deferred directives(親テンプレートのスコープで処理されるべき指示子、例: {{inlinecss}})に署名を付けるためのもの」と書かれています。

設計思想は正しいです。問題は「委ねられたディレクティブ」をどう見分けるかでした。

1つの比較式

上のコードの判定条件はこれです。

if ($result['directive'] === $result['output']) {

「ディレクティブを処理した結果が、処理前と同じ文字列なら、それは親へ委ねられたものだ」という推定です。{{inlinecss}}は子の中で$construction[0](ディレクティブそのもの)を返すので、たしかにこの条件を満たします。

しかし、この条件を満たすのは{{inlinecss}}だけではありません。フィルタが解決できなかったディレクティブもすべて、処理前と同じ文字列のまま残ります

ここで先ほどの継承構造が効いてきます。基底のFramework\Filter\TemplateにはblockDirectiveが存在しません。したがって、この基底フィルタで{{block class="..."}}を含む文字列を処理すると、{{block}}は解釈されず、そのままの形で出力されます。すると比較式が真になり、{{block}}に正規の署名が付きます

その署名付き文字列が、blockDirectiveを実装しているEmail\Model\Template\Filterに親として渡ると、2回目の走査で「署名がある、つまり子が意図的に委ねたものだ」と判断され、実行されます。

pingiun氏のgist(コミュニティによる根本原因の分析)は、この構図を次のようにまとめています。「Framework\Filter\TemplateにはblockDirectiveが無いので、顧客フィールド経由で届いた{{block}}は手つかずで通過し、委ねられたものと誤認され、署名される。Email\Model\Template\FilterblockDirectiveを実装しているので、親がそれを実行する」。

つまり、「弱いフィルタで処理された文字列が、強いフィルタに埋め込まれる」という経路さえあれば、署名機構が攻撃者の入力にお墨付きを与えてしまう。CWE-1336(テンプレートエンジンにおける特殊要素の不適切な無害化)そのものです。

{{block}}が実行されると何が起きるか

Email\Model\Template\Filter::blockDirective()の冒頭です。

// app/code/Magento/Email/Model/Template/Filter.php
public function blockDirective($construction)
{
    $skipParams = ['class', 'id', 'output'];
    $blockParameters = $this->getParameters($construction[2]);
 
    $block = null;
 
    if (isset($blockParameters['class'])) {
        $block = $this->_layout->createBlock($blockParameters['class'], null, ['data' => $blockParameters]);
    } elseif (isset($blockParameters['template'])) {
        $block = $this->_layout->createBlock(ElementTemplate::class, null, ['data' => $blockParameters]);
    }
    // ...
    foreach ($blockParameters as $k => $v) {
        if (in_array($k, $skipParams)) {
            continue;
        }
        $block->setDataUsingMethod($k, $v);
    }
    // ...
    return $block->{$method}();
}

classパラメータに書かれたクラス名がそのままcreateBlock()に渡り、残りのパラメータはsetDataUsingMethod()でsetterとして呼ばれます。そしてcreateBlock()の先にあるMagento\Framework\View\Element\BlockFactory::createBlock()は、修正前は次のようになっていました(ホットフィックスのdiffから復元)。

// vendor/magento/framework/View/Element/BlockFactory.php (修正前)
$block = $this->objectManager->create($blockName, $arguments);
if (!$block instanceof BlockInterface) {
    throw new \LogicException($blockName . ' does not implement BlockInterface');
}

先にオブジェクトを生成し、その後で型を検査する順序です。型検査で例外は投げられますが、その時点で任意クラスのコンストラクタは既に実行されています。MagentoのObjectManager::create()はDIコンテナなので、コンストラクタ引数も自動解決されます。コンストラクタや、DIで注入されるオブジェクトの初期化処理に副作用(ファイル書き込み、includeなど)を持つクラスを選べば、型検査をすり抜けなくても目的は達成できます。ホットフィックスがこれを「検査してから生成する」順序に改めていることが、この経路が悪用の一部だったことの裏付けです。

同じ「生成してから検査する」パターンはMagento\Backend\Model\Widget\Grid\Row\UrlGeneratorFactory::createUrlGenerator()にもあり、こちらも同時に修正されています。

stylesはどこに効いているのか

名前の由来であるstylesについて、一次情報で確認できることを並べます。

  • Sansecは「stylesプロパティを使うことで既存の防御を回避できる」と述べ、IOCとしてPOST /graphql?styles[....]=という形のリクエストを挙げています
  • ホットフィックスはMagento\Email\Model\AbstractTemplatesetTemplateStyles()setTemplateText()を新設し、文字列以外の入力を拒否するようにしました
// vendor/magento/module-email/Model/AbstractTemplate.php (ホットフィックスで追加)
public function setTemplateStyles($value)
{
    return $this->setData('template_styles', is_string($value) ? $value : '');
}

ここから読み取れるのは、template_stylesに文字列ではなく配列などが流れ込む経路があり、それが「テンプレートに対する既存の防御(顧客入力の変数がディレクティブとして実行されないためのガード)」を迂回する材料になっていた、ということです。ただし、GraphQLのstyles[...]パラメータがどのコードパスでtemplate_stylesに到達するのか、その正確な経路はSansecが続報で公開するとしており、執筆時点では未公開です。ここは推定であることを明記しておきます。

二段階の攻撃:汚染してから描画させる

Sansecは攻撃を2段階に整理しています。

  1. PHPコードを「注入(汚染)」する。例として、失敗レポートを生成させる
  2. 汚染したコードを、Magentoに「支払い失敗メール」を描画させることで実行させる

第1段階の「失敗レポート」とは、Magentoが致命的エラー時にvar/report/配下へ書き出すファイルのことです。REST/GraphQLで例外が起きたときはMagento\Framework\Webapi\ErrorProcessor::_saveFatalErrorReport()report/api/に、通常のWebリクエストならpub/errors/processor.phpsaveReport()が書き出します。レポートにはリクエスト内容が含まれるので、攻撃者がリクエストにPHPコードを含めて意図的にエラーを起こせば、そのPHPコードがサーバー上のファイルに書き込まれます

これは単なる推測ではなく、ホットフィックスが両方のファイルに対して「レポートの先頭に<?php exit; ?>を書き込み、レポート内容中の<?< ?に置換する」というガードを追加していることから裏付けられます。

// vendor/magento/framework/Webapi/ErrorProcessor.php (ホットフィックスで追加)
private const REPORT_EXECUTION_GUARD = '<?php exit; ?>';
 
protected function _saveFatalErrorReport($reportData)
{
    $this->directoryWrite->create('report/api');
    $reportId = abs((int)(microtime(true) * random_int(100, 1000)));
    if (is_string($reportData)) {
        $reportData = str_replace('<?', '< ?', $reportData);
    }
    $this->directoryWrite->writeFile(
        'report/api/' . $reportId,
        self::REPORT_EXECUTION_GUARD . PHP_EOL . $this->serializer->serialize($reportData)
    );
    return $reportId;
}

「先頭でexitする」ガードは、そのファイルがinclude / requireされても以降の内容が実行されないようにするための古典的な手法です。裏返せば、レポートファイルがPHPとして読み込まれる経路が存在したということです。Sansecの検知コマンドにgrep -ril 'x_trace_' var/report/が含まれているのも、レポートファイルが攻撃の中間生成物になっているためです。Sansecは第2の攻撃グループについて、Webシェルのドロッパーを「Store:リクエストヘッダから回収した。そのPHPタグは、Magentoがそれを記録したレコードからJSONエスケープされたままだった」とも書いており、ヘッダの値がレポートに保存されていたことがわかります。

第2段階の「支払い失敗メール」は、Magento_Checkoutモジュールのcheckout_payment_failed_template(ファイル名failed_payment.html)です。件名は"Payment Transaction Failed Reminder"。テンプレートの中身を見ると、顧客由来の変数が並んでいます。

<!--@subject {{trans "Payment Transaction Failed Reminder"}} @-->
<!--@vars {
"var billingAddressHtml|raw":"Billing Address",
"var customerEmail":"Customer Email",
"var customer":"Customer Name",
"var items|raw":"Shopping Cart Items",
"var reason":"Transaction Failed Reason",
...
} @-->
 
<h1>{{trans "Payment Transaction Failed"}}</h1>
<ul>
    <li>
        <strong>{{trans "Reason"}}</strong><br>
        {{var reason}}
    </li>
    <li>
        <strong>{{trans "Items"}}</strong><br>
        {{var items|raw}}
    </li>
    <li>
        <strong>{{trans "Billing Address:"}}</strong><br>
        {{var billingAddressHtml|raw}}
    </li>
    ...
</ul>

このメールは、チェックアウトで決済が失敗したときに管理者宛てに自動送信されるものです。顧客が決済を失敗させれば、顧客が入力した名前・住所・カート内容を含むテンプレートがサーバー側で描画されます。認証は不要です(ゲストチェックアウトが有効なら、アカウントすら不要)。Sansecが「誰もメールを開く必要はない。Magentoが描画する時点で悪意のあるコードが動く。メール配送が失敗しても攻撃は成立しうる」と書いているのは、実行のトリガーがメールの「送信」ではなく「描画」だからです。

2022年のTrojanOrder(CVE-2022-24086)も、注文フローの住所フィールドにテンプレートディレクティブを仕込んで実行させる手口でした。4年経って、同じ「チェックアウト経由でテンプレートに顧客入力を流し込む」入口が、別の穴(署名判定)を通じて再び開いた格好です。

ホットフィックスが変えた9ファイル

repo.magento.comから取得したVULN-39341_249.patchの変更対象を整理します。

ファイル変更内容
framework/View/Element/BlockFactory.phpDI設定から解決した型をis_a()で先に検査してからcreate()する(検査してから生成)
framework/View/Layout/Generator/Block.php上記のLogicExceptionも捕捉してログに落とす
module-backend/Model/Widget/Grid/Row/UrlGeneratorFactory.php同じく生成前にis_a()で型検査
framework/Webapi/ErrorProcessor.phpreport/api/のレポートに実行ガードを付与、<?を無害化
pub/errors/processor.phpvar/report/のレポートに実行ガードを付与、値を再帰的に無害化、読み込み時にガードを剥がす
module-email/Model/AbstractTemplate.phpsetTemplateText() / setTemplateStyles()を追加し文字列以外を拒否
module-email/Block/Adminhtml/Template/Preview.phpMagento_Email::templateのACLを検査してからプレビュー描画
module-newsletter/Block/Adminhtml/Template/Preview.phpMagento_Newsletter::templateのACLを検査
module-newsletter/Block/Adminhtml/Queue/Preview.phpMagento_Newsletter::queueのACLを検査

読んでわかるとおり、Adobeの修正は悪用チェーンの各段を個別に塞ぐものです。任意クラスのコンストラクタ実行を防ぎ、レポートファイルをPHPとして実行不能にし、template_stylesへの非文字列の流入を止め、プレビュー画面のACLを締める。

一方で、前述した署名判定の比較式そのもの($result['directive'] === $result['output'])には手が入っていません。Bigbridgeが公開している「deferred-directives fix」は、この点を「Adobeの公式パッチはこの問題を閉じていない」と指摘したうえで、Template::deferToParent()というメソッドで「意図的に委ねた」ことを明示的に記録し、記録されたディレクティブだけに署名する、という根本原因側の修正を提供しています。Adobeのパッチとはファイルの重なりが無いので併用できる、というのが同社の説明です。

// Bigbridge の deferred-directives fix (framework/Filter/Template.php への変更、抜粋)
if ($this->filteringDepthMeter->showMark() > 1 && $this->deferredDirectives) {
    $signature = $this->signatureProvider->get();
    foreach ($templateDirectivesResults as $result) {
        if ($result['directive'] === $result['output']
            && in_array($result['directive'], $this->deferredDirectives, true)
        ) {
            // 明示的に deferToParent() された指示子だけに署名

この根本原因の指摘と「Adobeの修正だけでは不十分」という主張は、執筆時点ではコミュニティ側の分析であり、Adobeが公式に認めたものではありません。ただ、「解決できなかった指示子を委ねられた指示子と同一視する」という判定が危ういこと自体は、上に引用した本体コードから読み取れる事実です。

実際に何が仕込まれたか - Sansecの観測

Rust製バックドア

攻撃が成功すると、バックグラウンドプロセスとしてバックドアが起動します。Sansecによれば「99.84.67.186のC2サーバーに接続してコマンドを待つ小さなRustプログラム」で、公開時点でSansec以外のセキュリティ企業には検知されていませんでした。プロセス名は環境に溶け込むように偽装され、9月4日からの数日で複数回変わっています。

日付プロセス名設置場所実装バージョン
9月4日[kworker/u:8:0](カーネルスレッドを偽装)/tmp/.kw_*~/.local/share/.gvfsd/gvfsd-user-
9月6日fc-cache(fontconfigのキャッシュ更新コマンドを偽装)~/.cache/fontconfig/fc-cache2.1.4
9月7日chronyd(NTPデーモンを偽装)/tmp/.chrony-<8hex>/chronyd2.1.5

C2通信の偽装が巧妙です。fc-cache版は60秒ごとにntp.timesync.toを名前解決し、UDP 123番ポートへ48バイトのパケットを送ります。NTPの応答に見える形式で、先頭4バイトだけが本物のNTP、残りはMessagePackでエンコードしたエージェントID、ホスト名、ユーザー名、OS、メモリとディスクの使用量、稼働時間、root権限の有無、インプラントのバージョンを運んでいます。Sansecの言葉を借りれば「ntp.で始まるホストへのUDP 123番なので、たいていの出口フィルタを何事もなく通過する」。

chronyd版はさらに一歩進んでいて、多くのLinuxディストリビューションで本物のNTPデーモンと同じ名前を使います。UDP 123番の通信を監視している人がchronydを除外した瞬間に、このビルドは完全に見えなくなります。Sansecは本物との見分け方を2つ挙げています。60秒ごとに9個の48バイトデータグラムを約10ミリ秒間隔で送る(本物のクライアントは1個)こと、そしてすべてのデータグラムがNTPv4のサーバーモードでマークされている(クライアントが送る理由が無い)ことです。

永続化の手口も注意が要ります。cronエントリ(13,43 * * * *57,27 * * * *)を書く場合と書かない場合があり、書く場合もcrontab -eではなくcronのスプールファイルへ直接書き込むため、syslogにREPLACEの行が残りません。chronyd版はcron無しで自己再起動できるので、「crontabが空だから安全」とは言えません。

第2の攻撃グループとPHP Webシェル

9月7日、Sansecは同じ被害店舗群で別グループの痕跡を見つけました。485バイトのPHPドロッパーで、商品画像のキャッシュディレクトリにWebシェルを書き込みます。

pub/media/catalog/product/cache/ss_<10hex>/sync_<10hex>.php

このシェルは、正しいX-Cache-Tokenヘッダが無いリクエストにはすべて404を返します。ヘッダ付きならtask POSTパラメータのPHPコードを実行します。ファイル名も配置場所も、画像キャッシュの一部に紛れるよう選ばれています。

ドロッパーの前には偵察が走ります。POST /graphqlに無害なクエリ(storeConfig { store_code })を載せ、PHPコードはStore:リクエストヘッダに入れるという形です。コードはカーネル文字列、PHPの実行ユーザー、作業ディレクトリ、pub/mediaの書き込み可否を集め、[a-z0-9-]に正規化して50文字ずつに切り、それぞれをサブドメインのラベルとしてoast.site(開発者がコード実行の確認に使う公開サービス)へDNSで送ります。レスポンス本文を見る必要が無く、DNSのコールバックログだけで結果を再構成できる。出口のHTTPを塞いでいてもDNSが通れば成立する、という手口です。

公開されているIOC

Sansecの記事から、種別ごとに抜粋します(2026年9月9日時点。記事は随時更新されています)。

種別指標
C2(WebSocket over TLS)99.84.67.186:443windwsecurity.run:443
C2(NTP偽装UDP)ntp.timesync.to:123ntp.timesysnc.net:123time.microsft.run:123pool.microsft.studio:123ntp.synctime.to:123ntp.syncstime.to:123
C2のAレコード(9月7日時点)185.157.160.251
マルウェア配布元247.cdnflare.xyz209.141.43.95www.incofar.it配下のパス
攻撃元IP88.216.72.181(複数の被害店舗で観測)、182.182.152.4876.31.99.207209.73.130.14877.239.124.107
窃取先ドメインcheckout-cdn.com
ハッシュ値バックドア5種とPHPドロッパーのSHA-256がSansecの記事に掲載
Webシェルの認証ヘッダX-Cache-Token: fced27f6d57702565353ecc11722533b
偵察のUser-AgentMozilla/5.0(偵察)、python-requests 2.15.0(インプラント操作者)

ドメイン名の綴りに注目すると、microsftwindwsecuritytimesysncのように正規サービス名の1文字違いが多用されています。ログを目視で追うときに見落としやすい選び方です。

同日KEV追加の4件

CISAは2026年9月8日、4件をまとめてKEVに追加しました。

CVE製品KEV上の脆弱性名是正期限
CVE-2026-75650Adobe Commerce and MagentoImproper Neutralization of Special Elements Used in a Template Engine2026年9月11日
CVE-2026-86218N-able N-centralStatic Code Injection2026年9月11日
CVE-2026-81963Microsoft WindowsLink Following2026年9月22日
CVE-2026-85880Microsoft WindowsHeap-Based Buffer Overflow2026年9月22日

Windowsの2件は同日の2026年9月Patch Tuesdayで修正されたゼロデイで、是正期限は2週間です。対してAdobe CommerceとN-able(8月にも別の認証バイパスがKEV入り)は3日。KEVのrequiredActionは「ベンダーの指示に従って緩和し、BOD 26-04(Prioritizing Security Updates Based on Risk)およびForensics Triage Requirementsに準拠すること。緩和策が無ければ製品の使用を中止すること」で、レコードのforensicTriageYesです。つまりCISAは連邦機関に対して、パッチ適用だけでなく「パッチ前に侵害されていなかったか」の調査を求めています。3日間のゼロデイ期間を考えれば当然の要求です。

過去のMagento大型脆弱性との比較

「Magentoでまた大きいのが出た」と言われる所以を、一次情報で確認できる範囲で並べます。

通称CVE速報(公開日)CVSS v3.1CWEKEV追加日何が起きたか
TrojanOrderCVE-2022-24086APSB22-12(2022年2月13日)9.8CWE-202022年2月15日チェックアウトの住所などにテンプレートディレクティブを注入。速報時点で「ごく限定的な攻撃」を確認
CosmicStingCVE-2024-34102APSB24-40(2024年6月11日)9.8CWE-6112024年7月17日XXE。Sansecは6月23日に悪用開始を確認し、6月18日時点で75%の店舗が未パッチと報告
SessionReaperCVE-2025-54236APSB25-88(2025年9月9日)9.1CWE-202025年10月24日セッションとREST APIのネストしたデシリアライズの組み合わせ。Sansecは10月22日に攻撃開始、11月1日までに81%の店舗が探索されたと報告
StyleSmugglerCVE-2026-75650APSB26-146(2026年9月7日)10.0CWE-13362026年9月8日テンプレート署名判定の穴。9月4日に悪用開始、9月7日にホットフィックス

4件を並べると、2つのことが見えます。

1つ目は、速報からKEV追加までの間隔が縮んでいること。 CosmicStingは36日、SessionReaperは45日かかっていますが、TrojanOrderとStyleSmugglerは2日と1日です。前者2件は「パッチ公開後に悪用が始まった」もの、後者2件は「速報時点で既に悪用されていた」ものという違いがそのまま出ています。

2つ目は、TrojanOrderとStyleSmugglerがどちらもテンプレートエンジン経由だということ。 TrojanOrderの修正以降、Magentoはテンプレートフィルタに署名やネスト深度の計測といった防御を追加してきました。StyleSmugglerはその追加された防御の判定ロジックを突いています。防御層を足すこと自体が新しい攻撃面を作るという、セキュリティ機構の設計でよく起きる構図です。

そしてCosmicStingとSessionReaperの数字が示すように、Magentoのエコシステムはパッチが出ても当たらない店舗が多数派です。今回も、Adobeがホットフィックスを出した事実より、「出てから何日で当たるか」のほうが被害規模を決めます。

対応手順

1.ホットフィックスVULN-39341を適用する(最優先)

CISAの是正期限は本記事公開日の2026年9月11日です。適用手順の骨子はExperience Leagueの記事に沿っています。

# 1. パッチzipを取得して展開(repo.magento.com の認証キーが必要)
#    VULN-39341-composer-patches.zip を展開すると、バージョン別の .patch が出てくる
 
# 2. 自分のバージョンに合う .patch を選び、Magento ルートから適用
git apply --check -p1 VULN-39341_249.patch   # まず dry-run
git apply -p1 VULN-39341_249.patch
 
# 3. Quality Patches Tool で適用状態を確認
vendor/bin/magento-patches -n status | grep "39341\|Status"

composerパッチプラグイン(cweagans/composer-patchesなど)で管理している環境では、composer.jsonextra.patchesに登録してcomposer installで当てる方法もあります。Adobe Commerce on cloud infrastructureの場合は、パッチをリポジトリのm2-hotfixes/ディレクトリに置いてデプロイします。

適用するパッチの選び方には注意点があります。zipに同梱されているのは各系統の最新パッチレベル向けのファイルです。2.4.8-p1から2.4.8-p3、2.4.7から2.4.7-p8、2.4.6-p11以前などを使っている場合は、Experience Leagueの記事に列挙されている個別のzipを使います。パッチが当たらない(hunk失敗)場合、無理に手で当てるのではなく、対象バージョンを確認し直してください。

2.サポート終了版を使っている場合

2.4.3以前にはAdobeの修正がありません。選択肢は2つです。

  • Scandiwebのバックポート(2.2.0から2.4.3-p3の41バージョン)を、ステージングで検証したうえで適用する。Sansecも本記事も内容は未検証です
  • これを機に2.4系のサポート中バージョンへ移行する

EOL版でこの規模の脆弱性が出るのは今回が初めてではありません。CosmicSting、SessionReaperのときも同じ状況でした。次も同じことが起きます。

3.暗号鍵と資格情報をローテーションする

Adobeは「完全に修復するには、暗号鍵だけでなく、その鍵で暗号化された、あるいは露出した可能性のあるすべての資格情報をローテーションせよ」としています。Experience Leagueの手順を要約します。

  1. ホットフィックスを適用し、メンテナンスモードを有効にしてcronを止める(cloudではvendor/bin/ece-tools cron:disable)
  2. 暗号鍵をローテーションする
  3. 管理者パスワード、統合トークン(REST / SOAP / GraphQL)、OAuthクライアントシークレットを再生成する
  4. 決済ゲートウェイのAPI資格情報、データベースの資格情報、SSH鍵とデプロイ鍵、サードパーティ拡張のAPIキーを更新する
  5. キャッシュをフラッシュし、cronを再開し、メンテナンスモードを解除する(cloudは再デプロイ)

Sansecが付け加えている注意が重要です。「Magentoの中だけでなく、発行元でローテーションすること。暗号鍵を回しただけでは、攻撃者が既に読み取ったものは無効にならない」。決済ゲートウェイのAPIキーは決済事業者の管理画面で、DBパスワードはDBサーバーで、SSH鍵は接続先で、それぞれ失効させる必要があります。

4.侵害されていないかを確認する

パッチは穴を塞ぐだけで、9月4日から7日の間に入られた店舗はきれいになりません。Sansecの公開している確認コマンドです。

# 偽装プロセスの有無
ps -eo pid,comm,args | grep -iE 'kworker|fc-cache|chronyd'
 
# 設置場所の確認
ls -la ~/.local/share/.gvfsd/ ~/.cache/fontconfig/fc-cache \
  /tmp/.kw_* /tmp/.cache_* /tmp/.gvfsd-* /tmp/.fc-*/fc-cache \
  /tmp/fc-cache /tmp/.chrony-*/chronyd 2>/dev/null
 
# cron(crontab -l だけでなくスプールファイル自体も見る)
crontab -l | grep -i gvfsd
grep -r 'crontab command not allowed' /var/log/
 
# 汚染されたレポートファイル
grep -ril 'x_trace_' var/report/
 
# メディアディレクトリ内の PHP ファイル(第2グループの Web シェル)
find pub/media -name '*.php'

[kworker/u:8:0]については、非rootユーザーが所有し、常駐メモリを持つ、角括弧付きのカーネルスレッド風の名前という組み合わせが目印です。本物のカーネルスレッドはrootが所有し、ユーザー空間のメモリを持ちません。

ネットワーク側では、UDP 123番の通信を宛先ごとに集計し、ntp.で始まる見慣れないドメインへの60秒周期の通信、あるいは9個連続の48バイトデータグラムを探します。DNSログではoast.siteへのサブドメインが大量に並ぶクエリが偵察の痕跡です。

もう1つ、Magento固有のシグナルとして「Payment Transaction Failed Reminderメールの突発的な増加」があります。正規の決済失敗でも同じメールは出るので単独では判断できませんが、9月4日以降にこのメールが急増していれば調査対象です。コミュニティの報告では、本文に{{var ...}}がそのまま残った壊れたメールが届くケースが観測されています。

侵害が確認された、あるいは否定できない場合、Sansecの推奨は「eComscanでスキャンし、資格情報を回す」です。ファイルを消してプロセスを殺すだけでは、cron無しで自己再起動するビルドや、まだ見つかっていない第3のペイロードに対処できません。ホストの再構築を選択肢に入れてください。

5. WAF / Webサーバー側の緩和(時間稼ぎとして)

disrex-groupが公開しているnginx / Apacheのルール例は、クエリ文字列にstyles[(URLエンコードならstyles%5B)、generatorClasswith_resolved、テンプレートディレクティブの開始記号、PHPの開始タグが含まれるリクエストを遮断するものです。ただし作者自身が「設計上バイパス可能」と明記しています。パラメータをPOSTボディに移されれば効きません。これらはあくまでホットフィックス適用までの時間稼ぎです。

6.コミュニティの根本原因パッチを検討する

Bigbridgeのdeferred-directives fix(2.4.5-p1以降と2.4.6系、2.4.7から2.4.9の2種類)は、Adobeのパッチとファイルが重ならず併用できる設計です。「Adobeの修正だけでは署名判定の問題が残る」という主張が正しければ、将来の変種に対する保険になります。ただしAdobe非公式であり、テンプレートの描画結果に影響しうる変更なので、ステージングでメールテンプレートとCMSページの表示を確認してから本番に入れてください。graycoreのモジュールは{{block}}で生成できるクラスを許可リスト方式に絞るもので、既定では何も許可しません。

恒久対策として考えたいこと

1つ目は、「解決できなかった」と「意図的に委ねた」を同一視しないこと。 署名判定の比較式は「処理結果が処理前と同じ」という状態から意図を推定していました。意図は明示的に記録するべきで、Bigbridgeの修正がdeferToParent()という形にしたのはそのためです。一般化すると、セキュリティ上の判断を「結果が変わらなかった」「例外が出なかった」のような消極的な条件で行わない、という原則になります。

2つ目は、生成してから検査しないこと。 BlockFactoryUrlGeneratorFactoryの「create()してからinstanceof」は、コンストラクタの副作用を無視した順序でした。DIコンテナが介在する環境では、クラス名を受け取った時点でis_a($className, Interface::class, true)のように文字列のまま検査するのが正しい順序です。PHPのis_a()は第3引数をtrueにするとクラス名文字列を受け付けます。

3つ目は、ログやレポートに書き出す内容を「あとでコードとして読まれるかもしれない」前提で扱うこと。 レポートファイルの先頭に<?php exit; ?>を置くガードは古典的ですが、Adobeがそれを今回入れたということは、これまで入っていなかったということです。エラーレポート、アクセスログ、キャッシュファイル。攻撃者が内容を制御できて、かつPHPプロセスがそのパスをincludeしうるものはすべて、書き込み時点で無害化するか、実行不能な場所と拡張子に置くべきです。

4つ目は、顧客入力がテンプレートに流れ込む経路を洗い出すこと。 TrojanOrderもStyleSmugglerも、入口は「顧客が自由に書けるフィールドが、管理者向けメールのテンプレート変数になる」という構造でした。名前、住所、商品のカスタムオプション、GraphQLの引数。これらがどの通知メールに載るのかを一覧化しておくと、次に似た脆弱性が出たときに影響範囲を即答できます。

5つ目は、パッチ適用にかかる日数を短くする体制。 CosmicStingでは公開1週間後に75%が未パッチ、SessionReaperでは攻撃開始4日で全店舗の49%が探索されました。StyleSmugglerのようにゼロデイから始まる場合、体制の差がそのまま被害の差になります。composerパッチを当ててテストして本番に出す流れが数時間で回せるかを、平時に確認しておく価値があります。国内でもECサイトの侵害は繰り返し起きており、2026年 日本のセキュリティインシデントまとめで追っています。

一次情報で確認できなかった事項

以下は本稿執筆時点で一次情報にあたっても確認できませんでした。断定を避け、未確認として記載します。

  • GraphQLのstyles[...]パラメータからテンプレート処理に至る正確なコードパス。Sansecは「完全なガジェットチェーンの解説は続報で公開する」としており未公開です。本文の機構説明のうち、template_stylesの非文字列拒否から逆算した部分は推定です。
  • 汚染したレポートファイルをPHPとしてincludeする具体的な経路。ホットフィックスの実行ガード追加と、コミュニティ分析(disrex-groupの「DIコンパイル時にincludeされる」という記述)から経路の存在は確実ですが、どのクラスのどのメソッドが読み込むのかは一次情報で確認できていません。
  • 被害店舗数。Sansecの記事に集計値の記載はありません。CosmicStingやSessionReaperのような割合の推計も、執筆時点では公表されていません。
  • 攻撃者の帰属。Sansecは2つの異なるグループの存在を示していますが、名称も帰属も挙げていません。
  • Bigbridgeの「Adobeの公式パッチは署名判定の問題を閉じていない」という主張に対するAdobeの見解。Adobeからの公式な言及は確認できていません。
  • Scandiwebのバックポートパッチの内容。ダウンロードがメールアドレス入力を要するため、本記事では取得・検証していません。
  • 修正を含む正式なパッチリリース(2.4.9-p1など)の予定。執筆時点でAdobeからの告知は確認できていません。

まとめ

  • CVE-2026-75650(StyleSmuggler)は、Adobe Commerce / Magento Open Sourceのテンプレートエンジンにおける未認証の任意コード実行。CVSS v3.1は満点の10.0、CWE-1336
  • 根本はFramework\Filter\Template::filter()の署名判定$result['directive'] === $result['output']。「親に委ねられた指示子」を見分けるための条件が「解決できなかった指示子」にも当てはまり、基底フィルタが解釈できない{{block}}正規の署名が付く
  • 署名付きの{{block}}blockDirectiveを持つ上位フィルタで実行され、BlockFactoryが「生成してから型検査」だったため任意クラスのコンストラクタが走る
  • 攻撃は二段階。エラーレポート(var/report)にPHPコードを書き込ませ、決済失敗時に管理者へ送られる"Payment Transaction Failed Reminder"メールの描画で実行させる。メールを誰かが開く必要はない
  • 2026年9月4日22:20 UTCに悪用開始、9月7日20:20 UTCにAdobeがAPSB26-146とホットフィックスVULN-39341を公開。3日間のゼロデイ
  • ホットフィックスは9ファイルの変更。生成前の型検査、レポートファイルへの<?php exit; ?>ガード、template_stylesの非文字列拒否、プレビュー画面のACL。署名判定の比較式そのものは変更されておらず、コミュニティが根本原因側の修正を別途公開している
  • 仕込まれたのはRust製バックドア。[kworker/u:8:0]fc-cachechronydと名前を変え、NTPを偽装したUDP 123番でC2と通信。cronスプールへの直接書き込みや、cron無しの自己再起動で永続化
  • 別グループがpub/media配下にPHP Webシェルを設置。偵察はStore:ヘッダにPHPを入れ、結果をDNSで持ち出す
  • CISAは9月8日にKEV追加、是正期限は3日後の9月11日、forensicTriageはYes。「パッチ前に入られていないか」の調査まで求めている
  • TrojanOrder(2022)、CosmicSting(2024)、SessionReaper(2025)に続く4年連続の大型事案。TrojanOrderと同じテンプレート経由で、TrojanOrder後に追加された防御の判定が穴になった

やることは3つです。ホットフィックスを当てる。暗号鍵と、その鍵で守られていたすべての資格情報を発行元で回す。そして9月4日以降に入られていないか、プロセス、cronスプール、var/reportpub/mediaを確認する。3日間パッチが無かった以上、「当てたから大丈夫」で終わらせないことが大切です。

参考リンク

2026年9月 Microsoft Patch Tuesday - ALPCとWindows Update Stackの悪用済みゼロデイ2件

2026年9月 Microsoft Patch Tuesday - ALPCとWindows Update Stackの悪用済みゼロデイ2件

42

2026年9月8日公開のMicrosoft月例更新を、MSRCのCVRFフィード・CISA KEV・NVDという一次情報で整理します。実悪用されたCVE-2026-81963(Windows Update Stackのlink following)とCVE-2026-85880(ALPCのヒープオーバーフロー)の技術的な中身、影響バージョンが両者で綺麗に分かれている理由、報道ごとに964〜999件と割れる修正件数の集計基準の違いまで。

Kestra CVE-2026-49869 - CVSS 10.0 の未認証RCEと、実際に踏まれたマイニング被害

Kestra CVE-2026-49869 - CVSS 10.0 の未認証RCEと、実際に踏まれたマイニング被害

51

ワークフロー基盤 Kestra の CVE-2026-49869(CVSS 10.0、CWE-78)を、GitHub Security Advisory・NVD・CISA KEV・Microsoft の観測レポートという一次情報で整理します。endsWith による末尾一致で認証フィルタが素通しになった原因、なぜ認証バイパスが即RCEになるのか、Dockerソケットが被害を増幅した経緯、2026年9月7日の追加修正までを追います。

SonicWall SMA1000 の CVE-2026-83548 / CVE-2026-83549 - CVSS 10.0 のSSRFと連鎖RCE

SonicWall SMA1000 の CVE-2026-83548 / CVE-2026-83549 - CVSS 10.0 のSSRFと連鎖RCE

35

SonicWall SMA1000 の2つのゼロデイ CVE-2026-83548(CVSS 10.0、CWE-918のプリ認証SSRF)と CVE-2026-83549(CWE-78、AMCのOSコマンドインジェクション)を、製品ノーティス SNWLID-2026-0016、CVEレコード、NVD、CISA KEV の JSON フィードという一次情報から整理します。連鎖して未認証RCEに至る構図、影響バージョンと修正ホットフィックス、7月の同型ゼロデイとの関係、確認手順とログ調査の観点までまとめます。