技術 約21分で読めます

KnowledgeDeliverの共通machineKeyでViewState RCEからGodzillaとCobalt Strikeまで到達

いけさん目次

TL;DR

影響 KnowledgeDeliverの2026年2月24日以前の導入環境。web.configのmachineKeyが共通のまま残っていればViewState RCEが成立(CVE-2026-5426)

対応 インスタンスごとに一意のmachineKeyを再生成。鍵変更だけでは侵害後の遺物(Godzilla Webシェル・改ざんJS・端末のCobalt Strike)は残る

追跡 Windows ApplicationログのEvent ID 1316(Event code 4009)とw3wp.exeからのcmd.exe/powershell.exe子プロセスが入口


デジタル・ナレッジのLMS「KnowledgeDeliver」で、ASP.NETのmachineKeyが複数導入先で共通になっていた。
MandiantがCVE-2026-5426として開示し、2025年後半にはゼロデイ(パッチ提供前に悪用された脆弱性)として実環境で使われていたと報告している。

攻撃はサーバー侵害で止まっていない。
ViewStateデシリアライズでIIS上のコード実行を取り、Godzilla(BLUEBEAM)をw3wp.exeのメモリ上で動かし、WebアプリのJavaScriptを書き換え、利用者端末にCobalt Strike Beaconを配布するところまで一本で繋がっている。

2月24日以前のweb.configが境目

Mandiantの開示では、影響条件は具体的に限定されている。
KnowledgeDeliverの導入が2026年2月24日より前で、ベンダー配布のweb.configに入っているmachineKeyを組織側で変更しておらず、アプリケーションが攻撃者から到達できる場合に成立する。

machineKeyはASP.NETがViewStateなどのデータを署名・暗号化するための鍵ペアで、validationKeydecryptionKeyの2つからなる。
個々の導入環境で一意の値を生成するのが前提だが、KnowledgeDeliverではベンダーが標準化したweb.configに固定値を入れて配布していた。
1つの導入先からweb.configを取得すれば、他の導入先にも有効な攻撃ペイロード(悪意あるコード本体)を作れてしまう。

2026年2月24日以降の更新手順で入れ直した環境と、古い導入を継続している環境では確認対象が違う。
オンプレミスや個別カスタマイズのLMSでは、製品ページ上の「最新」表示より、実際のweb.configに固定値が残っているかを直接見たほうが確実だ。

ViewStateデシリアライズの内部構造

ViewStateはASP.NET Web Formsがページの状態を保持する仕組みだ。
サーバーはページ内のコントロール状態をObjectStateFormatterでシリアライズし、Base64エンコードしたものを__VIEWSTATEという隠しフィールドとしてHTMLに埋め込む。
ブラウザはPOSTバックのたびにこの値を送り返し、サーバーが復号・検証・デシリアライズしてページ状態を復元する。

このときmachineKeyの2つの鍵が異なる役割を担う。

役割アルゴリズム例
validationKeyHMAC署名で改ざんを検知HMACSHA256, HMACSHA512
decryptionKeyViewState本体を暗号化AES-128, AES-256, 3DES

正規の流れでは、サーバーがPOSTされた__VIEWSTATEをBase64デコードし、decryptionKeyで復号し、validationKeyでMAC検証し、ObjectStateFormatterでデシリアライズしてページ状態を復元する。

ObjectStateFormatterは.NETのSystem.Web.UI名前空間に属するバイナリフォーマッターで、内部的には型トークンベースのシリアライズ形式を使う。
先頭に0xFF 0x01のマジックバイトがあり、続くバイトが型ID・フィールド長・値を繰り返す構造になっている。
型IDにはプリミティブ型(Int32Stringなど)のほかに、任意の.NET型をType.GetType()で解決する汎用型トークン(ID 0x02)がある。
この汎用型トークンの存在が、デシリアライズ時に任意クラスのインスタンス生成を許す根本原因だ。

web.configmachineKeyを指定する<machineKey>要素には、validation属性とdecryption属性でアルゴリズムを指定する。
.NET 4.5以降のデフォルトはvalidation="HMACSHA256" / decryption="AES"だが、古い構成ではSHA1 / 3DESの組み合わせもある。
攻撃者はweb.configから鍵と一緒にアルゴリズム指定も読み取れるので、一度web.configを入手すればペイロード生成に必要な情報が揃う。

ysoserial.netとガジェットチェーンの仕組み

攻撃者がmachineKeyを知っている場合、MAC検証を通る悪意あるViewStateを作れる。
ysoserial.netは.NETデシリアライズ攻撃用のペイロード生成ツールで、--plugin viewstateオプションでViewState形式のペイロードを直接生成する。

ysoserial.exe -p viewstate \
  -g TypeConfuseDelegate \
  -c "cmd /c whoami > C:\inetpub\wwwroot\test.txt" \
  --validationalg="SHA1" \
  --validationkey="<hex_key>" \
  --decryptionalg="AES" \
  --decryptionkey="<hex_key>" \
  --path="/KnowledgeDeliver/Default.aspx" \
  --apppath="/KnowledgeDeliver"

--path--apppathは重要で、ASP.NETのMAC計算にはページのvirtual pathが含まれる。
対象アプリケーションのURLパスが一致していないとサーバー側のMAC検証で弾かれるため、事前にURLの構造を把握しておかないとペイロードが通らない。

ガジェットチェーンとは、デシリアライズ時にオブジェクトのコンストラクタやプロパティセッターが連鎖的に呼ばれる仕組みを利用して、最終的にProcess.Start()Assembly.Load()に到達するオブジェクトグラフのことだ。
今回のケースで使われる代表的なチェーンを2つ掘り下げる。

TypeConfuseDelegateは、SortedSet<T>のデシリアライズ時にComparer(比較関数)が呼ばれる仕組みを悪用する。
Comparison<string>デリゲートの内部構造を、MulticastDelegate_invocationListを書き換えてProcess.Start(string)に差し替える。
SortedSetがデシリアライズ時に要素のソートを試みると、Comparerとして登録されたProcess.Startが呼ばれ、引数に渡されたコマンド文字列がOSコマンドとして実行される。

flowchart TD
    A["SortedSet&lt;string&gt;を<br/>デシリアライズ"] --> B["内部でSort()が発火"]
    B --> C["Comparerとして登録された<br/>デリゲートを呼び出し"]
    C --> D["MulticastDelegateの<br/>_invocationListを参照"]
    D --> E["実体はProcess.Start()に<br/>差し替え済み"]
    E --> F["引数の文字列が<br/>OSコマンドとして実行"]

TextFormattingRunPropertiesはWPF(Windows Presentation Foundation)のSystem.Windows.Markup名前空間にあるクラスで、XAMLマークアップをデシリアライズ時にパースする。
XAML内にObjectDataProviderを埋め込み、MethodNameStartObjectInstanceProcessを指定すると、XAMLのパース時点でプロセスが起動する。
このチェーンはWPFのアセンブリ(PresentationFramework.dll)がGACに存在するWindows Server環境であれば動作する。

どちらのチェーンもObjectStateFormatter形式で出力し、既知のvalidationKeydecryptionKeyで署名・暗号化すると、サーバー側のMAC検証をパスする。
検証が通った瞬間にObjectStateFormatterのデシリアライズが走り、ガジェットチェーンが発火してw3wp.exe内でコード実行に至る。

flowchart TD
    A["攻撃者:<br/>web.configからmachineKeyを入手"] --> B["ysoserial.netで<br/>ガジェットチェーンを生成"]
    B --> C["validationKey/decryptionKeyで<br/>署名・暗号化"]
    C --> D["__VIEWSTATEとして<br/>KnowledgeDeliverへPOST"]
    D --> E["ASP.NET:<br/>Base64デコード → 復号"]
    E --> F["MAC検証パス<br/>(pathも一致)"]
    F --> G["ObjectStateFormatterが<br/>デシリアライズ"]
    G --> H["ガジェットチェーン発火<br/>(TypeConfuseDelegate or<br/>TextFormattingRunProperties)"]
    H --> I["w3wp.exe内で<br/>任意コード実行"]

Microsoftは2025年2月に、コードリポジトリやドキュメントから3,000件以上の公開済みmachineKeyを確認したと報告している。
Stack Overflowの回答やサンプルコードに載っていた固定値がそのまま本番のweb.configに入り込むパターンで、KnowledgeDeliverの場合はベンダー配布の構成ファイル自体に共通鍵が入っていたという差分はあるが、「同じ鍵が複数環境で使われている」という根本構造は同じだ。

CVSSスコアの揺れ

The Hacker NewsはCVE-2026-5426をCVSS 7.5と報じている。
一方でNVDは記事作成時点で「Awaiting Enrichment」のままで、CISA-ADP由来のCVSS v3.1は9.1になっている。
MandiantのGitHub開示では別のベクトルでCVSS 9.0相当の記述があり、NVD側のNIST正式分析はまだ出ていない。

CVSS 7.5と9.1の差はAttack Complexityの評価による。
7.5は「machineKeyの入手に何らかの前提条件が必要」と見た評価、9.1は「同一製品のデフォルト鍵で攻撃者が事前に入手可能」と見た評価で、KnowledgeDeliverの場合は後者の解釈のほうが実態に近い。
同じ製品を導入している他の組織からweb.configを取得すれば鍵が手に入るため、Attack ComplexityはLowに分類するのが妥当だ。

脆弱性管理ツールがNVDのNIST側CVSSを待つ設計だと、CVEの存在とCWEは拾えても深刻度判定が遅れる。
NIST NVDのenrichment運用変更で書いた「CVEはあるがNVD分析がまだ」のパターンと同じ構造だ。

今回の優先度はCVSSスコアではなく実悪用の有無で判断する。
Mandiantが調査で確認し、Google CloudのThreat Intelligence記事として攻撃後の挙動まで公開している。
LMSがインターネットから到達可能なら、CVSSが確定するのを待たずに確認を始める。

Godzilla(BLUEBEAM)のメモリ内動作

ViewStateデシリアライズで最初に投入されるのがBLUEBEAM、つまりGodzillaの.NET版Webシェルだ。
Godzillaは開発者BeichenDreamが公開しているWebシェルフレームワークで、Java、PHP、ASP.NET(C#)、ASP(VBScript)に対応している。
KnowledgeDeliverはASP.NETアプリケーションなので.NET版のC#ペイロードが使われた。

ファイルレス実行の仕組み

従来のWebシェルはサーバーのディスクに.aspx.ashxファイルを書き込んで永続化するのが一般的だ。
BLUEBEAMはファイルを置かない。
ViewStateデシリアライズで得たコード実行権限を使い、CLR(Common Language Runtime)のAssembly.Load(byte[])を呼ぶ。
これは.NETアセンブリのバイト列をメモリ上に直接ロードするAPIで、ディスクにDLLファイルを書き出さずにコードを実行できる。

具体的な実行パスはこうなる。

  1. ガジェットチェーンがProcess.Start("powershell", "-enc <Base64>")で初期ローダーを起動
  2. 初期ローダーがHTTP経由でBLUEBEAMのアセンブリバイト列を取得
  3. Assembly.Load(byte[])でメモリ上にロード
  4. ロードされたアセンブリ内のエントリポイントがIISのHTTPモジュールとして自身を登録
  5. 以降はIISのリクエストパイプラインにフックし、特定のパス・パラメータへのリクエストをインターセプトしてC2(Command and Control、攻撃者の指令サーバー)通信に使う

HTTPモジュールとしての登録はHttpApplication.RegisterModule()またはDynamicModuleUtility.RegisterModule()で行われる。
IISのワーカープロセスw3wp.exeのアプリケーションドメイン内にモジュールが存在するため、プロセスが再起動されるまで生存する。
逆に言えば、w3wp.exeのリサイクル(デフォルトでは1,740分間隔またはメモリ閾値超過時)が起きると消える。
攻撃者はリサイクル後に再投入する手段を別途持っているか、リサイクル間隔内に目的を達成する必要がある。

C2通信プロトコルの詳細

GodzillaのC2通信はHTTP POSTで、リクエストボディに暗号化されたコマンドを入れる。
暗号化方式はAES-128-CBC、鍵はデプロイ時に攻撃者が設定するパスフレーズのMD5ハッシュ前半16バイトだ。

通信フォーマットの構造は以下のようになっている。

[リクエスト]
POST /KnowledgeDeliver/api/handler HTTP/1.1
Content-Type: application/x-www-form-urlencoded

data=<Base64(AES-CBC-Encrypt(payload))>

[ペイロード構造]
+--------+--------+------------------+
| 2 byte | 4 byte | N byte           |
| cmd_id | length | serialized_args  |
+--------+--------+------------------+

cmd_idはモジュールの機能IDで、ファイル一覧取得、ファイルダウンロード、任意コマンド実行、アセンブリロードなどに対応する。
レスポンスも同じAES鍵で暗号化して返す。
外形的には通常のHTTP POSTリクエストと区別がつかない。
Content-Lengthとレスポンスサイズの比率が異常に大きい場合(ファイルダウンロード時など)だけネットワーク監視で引っかかる可能性がある。

モジュールアーキテクチャ

Godzillaはモジュール方式で機能を拡張する。
攻撃者のGUIクライアントからモジュールの.NETアセンブリバイト列をPOSTで送り込み、サーバー側のBLUEBEAMがAssembly.Load(byte[])でロードしてInvoke()する。
モジュール自体もディスクに残らない。

標準モジュールは以下の機能を持つ。

モジュール機能検出可能性
CmdShellcmd.exe /cでコマンド実行w3wp.exeの子プロセスとしてcmd.exeが出現
FileManagerファイルのCRUD操作ファイルシステム監査ログ(4663)で捕捉可
AssemblyLoader任意の.NETアセンブリをロードAMSI有効環境ではスキャン対象
DatabaseManager接続文字列を使いDB操作DBのクエリログで捕捉可
ScreenCaptureデスクトップのスクリーンショット取得GUIセッションがないサーバーでは失敗する

CmdShellモジュールを使った時点でw3wp.execmd.exeの親子関係が発生する。
これが後述する検出の主要な手がかりになる。

JS改ざんからCobalt Strike配布へ

BLUEBEAMでサーバー上のコード実行を確保した後、攻撃者はWebアプリケーションのJavaScriptを改ざんしている。

権限昇格の手順

まずicaclsでWebアプリケーションディレクトリにEveryoneのフルアクセス権限を付与する。

icacls C:\inetpub\wwwroot\KnowledgeDeliver /grant Everyone:F /T

IISのコンテンツディレクトリは通常IIS_IUSRSIUSRに読み取り専用、アプリケーションプールIDに限定的な書き込み権限を設定する。
w3wp.exeのワーカープロセスアカウント(アプリケーションプールID)は多くの場合IIS AppPool\<PoolName>という仮想アカウントで動いており、Webルート全体への書き込み権限は持っていない。
icaclsでEveryoneにフルアクセスを開けることで、どのプロセスからもファイルを書き換えられるようにした。

このicacls呼び出し自体がCmdShellモジュール経由なので、w3wp.execmd.exeicacls.exeというプロセスチェーンが発生する。
SeSecurityPrivilegeの監査が有効であればSecurity Event 4670(Permissions on an object were changed)にも記録される。

注入コードの構造

改ざん対象はアプリケーションの共通ライブラリJS(全ページで読み込まれるファイル)だ。
注入されたコードは2つの機能を持つ。

1つ目は偽のセキュリティ警告ダイアログだ。
DOMに<div>をオーバーレイで挿入し、「security authentication plugin」のインストールを促す文言と「Download」ボタンを表示する。
ダイアログはCSS position:fixed; z-index:99999で全画面を覆い、閉じるボタンを押しても数秒後に再表示するループが入っている。
ユーザーに「インストールしないとLMSが使えない」と思わせる設計だ。

2つ目は外部スクリプトローダーで、攻撃者が管理するドメインから追加のJavaScriptを<script>タグの動的挿入で読み込む。
このスクリプトが環境判別(OS、ブラウザ、ユーザーエージェント)を行い、条件に合う端末にだけダウンロードリンクを表示する。
Mandiantの報告では、外部ドメインはLet’s Encryptの証明書を取得しており、HTTPS通信になっていた。

Cobalt Strike Beaconの配布と動作

LMSの利用者がこのページを開くと偽警告が表示され、指示に従って「プラグイン」をダウンロード・実行するとCobalt Strike Beaconが端末にインストールされる。

Cobalt Strikeはペネトレーションテスト向けに開発されたC2フレームワークで、Beaconはそのエージェントにあたる。
BeaconのC2通信にはHTTPS、DNS、SMBの3つのチャネルがあり、今回はHTTPSが使われた。

Beaconは以下のライフサイクルで動作する。

  1. ダウンロードされたEXE(またはDLL sideloading構成)がステージャー(本体をダウンロードする中間ローダー)として動作し、C2サーバーからフルのBeaconペイロード(約200-300KB)を取得する
  2. ステージャーが取得したペイロードをVirtualAlloc()で確保したRWXメモリ領域に書き込み、そこにジャンプして実行を開始する
  3. Beaconはデフォルトではrundll32.exedllhost.exeのような犠牲プロセスを起動し、そこに自身を注入して本体プロセスを終了する。注入手法はCreateRemoteThread、QueueUserAPC、NtMapViewOfSectionなどから選択可能
  4. 設定されたスリープ間隔(通常60秒程度)でC2サーバーにHTTPS GETを送り、実行すべきタスクがあればレスポンスで受け取る。スリープ間隔にはjitter(ランダムな揺らぎ、通常10-30%)が加わる

malleable C2プロファイルと呼ばれる設定ファイルで通信パターンを細かくカスタマイズできる。
HTTP GETのパス、ヘッダー、Cookieの形式、レスポンスの変換(Base64、XOR、prepend/append)を定義し、正規のCDNトラフィックやAPI通信に見せかける。
たとえば/api/v2/statusのようなパスでJSONレスポンスに見える形式にすると、URLフィルタリングやSSLインスペクションを通過しやすい。

Mandiantによると、今回のBeaconのペイロードは侵害された組織の名称を鍵として暗号化されていた。
具体的には組織名の文字列からSHA256ハッシュを取り、その先頭16バイトをAES-128鍵としてステージャー内のBeaconペイロードを暗号化している。
これにより、万一ステージャーが他の分析環境で取得されても正しい組織名を知らなければペイロードが復号されない。
サンドボックス解析やVirusTotalへのアップロード時にBeaconの設定(C2ドメイン、通信パターン)が露出しないための耐解析手法だ。
分析者は侵害対象の組織名が分かっていれば復号できるので、インシデントレスポンスの現場では問題にならないが、第三者による事前の脅威インテリジェンスを妨害する。

flowchart TD
    A["共通machineKeyを入手"] --> B["署名済みの悪意ある<br/>ViewStateを作成"]
    B --> C["KnowledgeDeliverへ<br/>__VIEWSTATEをPOST"]
    C --> D["ASP.NETがデシリアライズ<br/>→ コード実行"]
    D --> E["w3wp.exe内で<br/>BLUEBEAMを起動"]
    E --> F["CmdShellでicaclsを実行<br/>→ Webディレクトリの権限開放"]
    F --> G["ライブラリJSに<br/>偽警告と外部ローダーを注入"]
    G --> H["LMS利用者が<br/>偽セキュリティ警告を受信"]
    H --> I["ステージャーをダウンロード・実行"]
    I --> J["C2からBeaconペイロード取得<br/>(組織名で暗号化)"]
    J --> K["プロセスインジェクションで<br/>犠牲プロセスに移住"]
    K --> L["HTTPS経由で<br/>C2ビーコニング開始"]

Webアプリ侵害から訪問者の端末へ被害が広がる流れは、ファイルアップロードRCEでWebシェルを設置したMagento PolyShellの記事の事例と構造が近い。
入口は違うが、公開Webアプリ上で任意コード実行を取った後にWebルートやJavaScriptを触って利用者側へ攻撃を展開するパターンは共通している。

Event ID 1316からの追跡

Mandiantが示しているハンティングの起点は、Windows ApplicationログのEvent ID 1316だ。
ソースはASP.NET 4.0.30319.0で、Event code 4009としてViewState検証失敗が記録される。

2種類の失敗パターン

1つ目のintegrity check failure(MAC検証失敗)は、攻撃者が間違った鍵でViewStateを作成した試行を示す。
イベントのメッセージ本文に「Validation of viewstate MAC failed」という文字列が含まれる。
鍵が合っていないのでデシリアライズには進まず、この段階では実害は出ていない。
ただし、正規ユーザーの操作で発生することはほとんどない。
ロードバランサーの背後でセッションアフィニティが崩れた場合や、machineKey変更直後のキャッシュ残り期間に起きることはあるが、継続的に出ている場合はスキャンか攻撃試行だ。

2つ目のinvalid ViewStateは、MAC検証を通過した後のデシリアライズ段階でのエラーを示す。
メッセージに「Invalid viewstate」が含まれる。
鍵が正しかったがペイロードの形式に問題があったケース(ガジェットチェーンのバージョン不一致など)と、デシリアライズ自体は成功してコード実行に至った後のエラーの両方がありうる。
こちらが見つかったらすぐに深掘りする。

ログの突き合わせ手順

ログだけでは実行成功を断定できない。
Mandiantは、サーバーのmachineKeyを使ってイベントログ内のペイロード文字列を復号し、BLUEBEAM関連のコードを回収している。
Event ID 1316が出ていたら、以下の4つのログを同じ時間帯で突き合わせる。

IIS W3Cアクセスログ (C:\inetpub\logs\LogFiles\W3SVC<n>\)
Event ID 1316と同時刻のPOSTリクエストを特定する。
cs-methodがPOST、cs-uri-stemがKnowledgeDeliverのページパス、cs-bytes(リクエストサイズ)が通常より大きいエントリを探す。
正規のViewStateは数KB程度だが、ガジェットチェーンを含むペイロードは数十KBになることがある。
c-ip(ソースIP)の記録が攻撃元の特定になる。

Windows Applicationログ
Event ID 1316のほか、Event ID 1309(Event code 3005: Unhandled exception)もチェックする。
ガジェットチェーンの実行中に例外が発生した場合、スタックトレース内にTypeConfuseDelegateTextFormattingRunPropertiesの型名が出ることがある。

プロセス生成イベント(Sysmon Event ID 1またはSecurity Event 4688)
w3wp.exeからの子プロセス起動を拾う。
正常なIIS運用でw3wp.execmd.exe /cwhoamipowershell.exeicacls.exeを直接呼ぶことはまずない。

Sysmonが入っている環境での検索クエリ例。

<QueryList>
  <Query Id="0" Path="Microsoft-Windows-Sysmon/Operational">
    <Select Path="Microsoft-Windows-Sysmon/Operational">
      *[System[(EventID=1)]]
      and
      *[EventData[Data[@Name='ParentImage'] and (Data='C:\Windows\System32\inetsrv\w3wp.exe')]]
    </Select>
  </Query>
</QueryList>

EDRのKQLクエリ例(Microsoft Defender for Endpoint)。

DeviceProcessEvents
| where InitiatingProcessFileName == "w3wp.exe"
| where FileName in ("cmd.exe", "powershell.exe", "whoami.exe", "icacls.exe", "net.exe", "certutil.exe")
| project Timestamp, DeviceName, FileName, ProcessCommandLine, InitiatingProcessCommandLine

ファイルシステム変更イベント
Sysmon Event ID 11(FileCreate)やEvent ID 2(File creation time changed)でWebルート配下の.jsファイル変更を検出する。
Webルートのファイルがw3wp.exeのプロセスによって変更されている場合は高確度のIOC(Indicator of Compromise、侵害の痕跡)だ。

User-Agentの異常

Mandiantは、2つのブラウザ識別子を連結したような不自然なUser-Agent文字列を報告している。
たとえばMozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/120 ... Chrome/119 ...のようにChromeのバージョン番号が2つ含まれるパターンだ。
GodzillaのHTTPクライアントがUser-Agentを組み立てる際のバグ、またはテンプレートの結合ミスと思われる。

正常なブラウザ更新でもUser-Agentは変化するが、Event ID 1316、w3wp.exeの子プロセス、JS改ざん、異常なUser-Agentが同じ期間に集中しているなら、単発のスキャンではなく侵害後の活動として扱う。

鍵ローテーション後に残る作業

machineKeyの再生成

最初にやることは、KnowledgeDeliverインスタンスごとに一意で十分な強度のmachineKeyを生成し直すことだ。
validationKeyは64バイト(128文字hex)以上、decryptionKeyは24バイト(48文字hex、3DES用)または32バイト(64文字hex、AES-256用)が推奨される。
PowerShellで暗号論的に安全な乱数から生成できる。

# validationKey (64 bytes = 128 hex chars, HMACSHA256用)
$vk = -join ((1..64) | ForEach-Object {
    '{0:X2}' -f (Get-Random -Minimum 0 -Maximum 256)
})
# ↑ 本番ではRNGCryptoServiceProviderを使う:
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
$bytes = [byte[]]::new(64)
$rng.GetBytes($bytes)
$vk = [System.BitConverter]::ToString($bytes).Replace('-','')

# decryptionKey (32 bytes = 64 hex chars, AES-256用)
$bytes = [byte[]]::new(32)
$rng.GetBytes($bytes)
$dk = [System.BitConverter]::ToString($bytes).Replace('-','')

web.configへの設定例。

<machineKey
  validationKey="<生成した128文字hex>"
  decryptionKey="<生成した64文字hex>"
  validation="HMACSHA256"
  decryption="AES"
/>

古い共通鍵を残したまま、WAFやアクセス制限だけを足しても、同じ鍵を使ったViewStateペイロードは有効なままだ。
また、AutoGenerate(ASP.NETの自動生成モード)に切り替える場合、ロードバランサー配下で複数台のIISを使っている構成では全サーバーで同じ鍵が必要になるため、明示的な鍵指定のほうが管理しやすい。
AutoGenerate,IsolateAppsを使うとサーバーごとに鍵が異なり、セッションアフィニティが崩れるとViewState検証が失敗する。

侵害の痕跡確認

鍵の変更は入口を塞ぐだけで、侵害後に設置されたものは消えない。
以下の3層で確認が必要だ。

サーバー層
Microsoftは2025年2月のASP.NET machineKey悪用に関する記事で、ASP.NET 4.8へアップグレードしてAMSI(Antimalware Scan Interface)を有効にすることを推奨している。
AMSIを有効にすると、Assembly.Load(byte[])で読み込まれるアセンブリがWindows Defenderなどの登録済みAMSIプロバイダーによってメモリ上でスキャンされる。
BLUEBEAMのようなファイルレスのWebシェルも、ロードの瞬間にバイト列がスキャンされるため検出対象になる。
ただしAMSIバイパス手法(AmsiScanBufferのパッチ、CLRバージョン偽装など)は攻撃者側も把握しているので、AMSIだけに頼るのは危険だ。

WindowsのAttack Surface Reduction(ASR)ルールでは「Block webshell creation for Servers」(GUID: a8f5898e-1dc8-49a9-9878-85004b8a61e6)が対応する。
このルールはw3wp.exeからの.aspx/.ashx/.phpファイル作成をブロックするが、BLUEBEAMはファイルを作らないので直接的な防御にはならない。
ただし攻撃者がバックアップとしてディスク上にWebシェルを設置しようとした場合のセーフティネットとして有効だ。

Webアプリ層
Webルート配下の.js.aspx.configファイルの整合性を確認する。
変更管理でハッシュを取っている環境なら、改ざん前の状態とdiffを取る。
ライブラリJSに外部ドメインへの<script src="https://...">タグ、document.createElement('script')、見慣れないfetch/XMLHttpRequestの追加がないかを見る。

改ざんされたJSは偽警告と外部スクリプトローダーを含むため、ファイルのリストアだけでなく、改ざんJSが配信されていた期間にLMSにアクセスした利用者への告知も判断材料になる。

端末層
Cobalt Strike Beaconが端末に入っている場合、以下で感染範囲を特定する。

  • C2通信先ドメインへのDNSクエリをDNSサーバーのログまたはプロキシログで確認
  • HTTPS接続先の証明書(Subject, Issuer, JA3ハッシュ)をTLSインスペクションログで確認。Cobalt Strikeのデフォルト証明書は特徴的なSubject(CN=Major Cobalt Strikeはデフォルト設定、カスタム証明書でも自己署名のものが多い)
  • プロセスツリーでステージャーのEXE名、注入先のプロセス(rundll32.exedllhost.exeが無引数で起動)を確認
  • 端末のネットワーク接続で、スリープ間隔に近い周期(60秒前後)で同一ホストへの接続が繰り返されていないか確認

ネットワーク側の対策

インターネットからLMSへ直接到達できる構成なら、到達元の制限も入れる。
教育機関や社外受講者向けLMSでは単純なIP制限が難しいこともあるが、管理画面・教材作成画面と受講者向け画面でネットワーク的な露出を分けられるかは確認する。

ViewStateデシリアライズ攻撃はPOSTリクエストで成立するため、WAFで以下の検知ルールを入れる手もある。

  • __VIEWSTATEパラメータのサイズが閾値(正規の最大値+マージン)を超えるPOSTをブロック
  • Content-Type: application/x-www-form-urlencoded__VIEWSTATEを含むPOSTのレート制限
  • 既知のysoserial.netガジェットチェーンに含まれるBase64パターンのシグネチャマッチ

ただし正規のViewStateも複雑なページでは大きくなりうるので誤検知との兼ね合いになる。
また、ASP.NET Core側のCVE-2026-40372のような別の攻撃面がASP.NET系には存在するため、ViewState単体の対策だけでは不十分な場合もある。

参考