セキュリティ
ViewStateメッセージの復号
Decrypting View State Messages (zeroed.tech)
要約
この記事は、暗号化されたViewStateメッセージを復号する方法について解説しています。特に、自動生成されたキー(autogen keys)から最終的なマシンキーを導出し、レガシーおよびモダンな暗号化構成の両方でViewStateメッセージを復号するプロセスに焦点を当てています。
全文翻訳
ViewStateメッセージの復号 公開日: 2026/01/23 最近、興味深い問題を抱えた人から連絡がありました。その人は、Windowsアプリケーションログで、おそらく悪意のあるViewStateを含む1316イベントを発見しました。ただ一つの問題は、それが暗号化されていたことでした。さらに悪いことに、アクセスできたのはホストのディスクイメージだけでした。影響を受けたサイトのweb.configファイルを抽出したところ、侵害されたサイトは自動生成されたキーで構成されていたことがわかりました。彼らはWindowsレジストリから自動生成キーをダンプできましたが、それらを使ってViewStateを復号する方法を知りませんでした。ViewStateの復号の複雑さは、レガシー設定では(バッファの末尾から検証ハッシュを削除し、正しいキーと対称アルゴリズムで復号するだけで)単純なものから、現代の設定では複雑なものまであります。現代の設定では、これまでリフレクションを使用してIISを「騙して」値を復号させてきました。私は通常、レガシーViewStateの復号にはCyberChefを、現代のViewStateの復号にはBlacklist3rを利用しています。個人的には、ViewStateの復号に両方のツールを使うのは少し面倒なので、この記事の最後に新しいツールを紹介します。元の問題に戻ると、暗号化されたViewStateがレガシーかモダンかに関わらず、自動生成キーを持っていてもあまり役に立ちません。復号するには最終的なマシンキーが必要です。しかし、レジストリまたはLSAシークレットに保存されている自動生成キーブロブから、実際に使用できる復号キーをどのように取得するのでしょうか?この記事は、ViewState、アクティブに悪用されているパッチ不可能なIISの永続的な脆弱性、という記事の続編です。この前の記事を読むことを強くお勧めします。前の記事では、キー生成プロセスを大まかに説明し、キーの一意性をアプリケーション間で保証するために渡すことができるモディファイアについて言及しましたが、その記事は主にレガシー(しかし依然として支配的)な暗号化構成に焦点を当てており、アプリが移行すべきモダンな構成についてはほとんど触れませんでした。この記事では、以下の点について説明します。自動生成キーがどのように生成されるかマスターマシンキーが自動生成キーからどのように派生するか最終マシンキーがマスターマシンキーからどのように派生するか最終キーを使用してViewStateメッセージを復号する方法そして今回は、レガシーとモダンな暗号化構成の両方について、これらすべてをカバーします。自動生成キーとは何か、そしてどのように生成されるのか?「自動生成キー」という名前は、実際にはキーではないため、あまり良い選択ではないように思えますが、Microsoftはコードとレジストリの両方でそのように参照しているため、ここではそれに従います。自動生成キーは、IISのマスター検証および復号マシンキーを特定のオフセットに含む1024バイトのデータブロブです。これは、レジストリまたは(暗号化された)LSAシークレットに保存され、実行時に読み取られる値です。System.Web.HttpRuntime::SetAutogenKeys() でこの「キー」がどのように生成されるかを確認できます。この記事で言及されているすべてのNamespace.Class::Functionの組み合わせは、.NET FrameworkのSystem.Webアセンブリで見つけることができます。
```csharp
internal static byte[] s_autogenKeys = new byte[1024];
private static void SetAutogenKeys()
{
byte[] array = new byte[HttpRuntime.s_autogenKeys.Length];
byte[] array2 = new byte[HttpRuntime.s_autogenKeys.Length];
bool flag = false;
RNGCryptoServiceProvider rngcryptoServiceProvider = new RNGCryptoServiceProvider();
rngcryptoServiceProvider.GetBytes(array);
if (!flag)
{
flag = UnsafeNativeMethods.EcbCallISAPI(IntPtr.Zero, UnsafeNativeMethods.CallISAPIFunc.GetAutogenKeys, array, array.Length, array2, array2.Length) == 1;
}
if (flag)
{
Buffer.BlockCopy(array2, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);
return;
}
Buffer.BlockCopy(array, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);
}
```
dbSpyによって生成された変数名はあまり良くないので、掘り下げる前に少し整理しましょう。
```csharp
internal static byte[] s_autogenKeys = new byte[1024];
private static void SetAutogenKeys()
{
byte[] randBytes = new byte[HttpRuntime.s_autogenKeys.Length];
byte[] autogenKey = new byte[HttpRuntime.s_autogenKeys.Length];
bool existingKeyLoaded = false;
RNGCryptoServiceProvider rngcryptoServiceProvider = new RNGCryptoServiceProvider();
rngcryptoServiceProvider.GetBytes(randBytes);
if (!existingKeyLoaded)
{
existingKeyLoaded = UnsafeNativeMethods.EcbCallISAPI(IntPtr.Zero, UnsafeNativeMethods.CallISAPIFunc.GetAutogenKeys, randBytes, randBytes.Length, autogenKey, autogenKey.Length) == 1;
}
if (existingKeyLoaded)
{
Buffer.BlockCopy(autogenKey, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);
return;
}
Buffer.BlockCopy(randBytes, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);
}
```
はるかに良いです。SetAutogenKeysが呼び出されるとき(HttpRuntimeの初期化の一部として発生します)、2つの1024バイト配列が定義されます。最初の配列randBytesはランダムなデータで埋められ、2番目の配列はデフォルトの初期化値0を保持します。両方の配列がEcbCallISAPI(「Extension Control Block Call Internet Service API」の略だと思われます)関数とCallISAPIFunc.GetAutogenKeys定数とともに渡されます。EcbCallISAPIは、自動生成キーの取得を含む、いくつかの内部IIS関数へのブリッジとして機能します。ここでネイティブコードに入り始めているので、高レベルに保ちましょう。この関数は、ローカルセキュリティ機関(LSA)やWindowsレジストリのいくつかの場所から既存の自動生成キーをロードしようとします。既存のキーが見つかった場合、それは渡されたautogenKeyバイト配列にコピーされますが、既存のキーが見つからない場合は、randBytesの内容が新しく生成された自動生成キーとして扱われ、現在のIISアプリケーションプールの実行権限に応じて、ローカルセキュリティ機関またはWindowsレジストリに格納されてから返されます。このプロセスについては、前の記事の「キー生成」セクションを参照してください。最後に、autogenKeyまたはrandBytesの内容のいずれかがs_autogenKeys配列にコピーされ、アプリケーション全体で使用できるようになります。自動生成キーからIISマシンキーへ自動生成キーがどのように生成されるかを理解したので、この巧妙なバイト配列からIISマシンキーがどのように派生するかを見てみましょう。以降、レガシーとモダンな暗号化の両方について各トピックをカバーします。レガシー - マスターマシンキー生成キー生成、またはむしろキー抽出は、System.Web.Configuration.MachineKeySection::RuntimeDataInitialize() で行われます。マスター検証キーは単に自動生成キーの最初の64バイトであり、マスター復号キーはそれに続く24バイトです。
```csharp
private static int _AutoGenValidationKeySize = 64;
private static int _AutoGenDecryptionKeySize = 24;
private byte[] _ValidationKey;
private byte[] _DecryptionKey;
private void RuntimeDataInitialize(){
....
// Validation Key Extraction
Buffer.BlockCopy(HttpRuntime.s_autogenKeys, 0, this._ValidationKey, 0, MachineKeySection._AutoGenValidationKeySize);
....
// Decryption Key Extraction
Buffer.BlockCopy(HttpRuntime.s_autogenKeys, MachineKeySection._AutoGenValidationKeySize, this._DecryptionKey, 0, MachineKeySection._AutoGenDecryptionKeySize);
....
}
```
例えば、次の自動生成キーを考えます。
```
726E2B4BF328A110EBBA089F1ED4A347CB45FFA9D2164C9D7510320A96500330FA7D2516FC41CD03A925FD242979CFE08551B59846B21436B38A156D46C1FB6AFD0A2D2785BFE3F5C747A3E2FF29AFF77EFBC2852C26B93685FB784A613372491F668C85403F1427B409BE784B5E7142C0D8ABA49803463408D0661453EE1681D6E91EB9FEB846A40F21B8CADB347AD291134191B7FA6D44EFAC1718ED460EDEF50EFC782E9AB97B1CA948CD259B26FDEED6DA36CBC37083A82C96B58989918270204CAD473DC721A4F8D4412250B25582080064D05B1A964FFBBC5E9552918[TRUNCATED]
```
マスター検証キーは726E2B4BF328A110EBBA089F1ED4A347CB45FFA9D2164C9D7510320A96500330FA7D2516FC41CD03A925FD242979CFE08551B59846B21436B38A156D46C1FB6Aとなり、マスター復号キーは...