2023年4月11日にMicrosoftがリリースしたセキュリティアップデートでは、90以上の個別の脆弱性が対処されました。特に注目すべきは、QueueJumperと呼ばれるCVE-2023-21554で、これはMicrosoft Message Queueing(MSMQ)サービスに影響を及ぼすリモートコード実行の脆弱性です。MSMQは、アプリケーションがローカルとリモートの両方で到達可能なメッセージ・キューを介してメッセージを交換できるようにするオプションのWindowsコンポーネントです。この分析は、Valentina Palmiotti、Fabius Watson、Aaron PortnoyがRandoriおよびX-Force Adversary Servicesチームと共同で実行しました。
当初、CVE-2023-21554の次の性質が注目を集めました。
AI活用のグローバル・トレンドや日本の市場動向を踏まえたDX、生成AIの最新情報を毎月お届けします。登録の際はIBMプライバシー・ステートメントをご覧ください。
MSMQの存在を検知する方法を決定するために、ラボ環境でコンポーネントを使用してテストシステムを構成しました。MSMQエンドポイントのポート1801に接続しているとき、サーバーはデータで応答しませんでした。サーバーは適切に形式されたデータを受信しないと応答しないと想定できます。エンドポイントとの会話を実行する方法を理解するには、基盤となるコードをさらに検査する必要がありました。
アドバイザリーからは、どのバイナリーにパッチが適用されたかはすぐには明確ではありません。変更されたコードを特定するための最初のステップは、更新ファイルをダウンロードすることでした。これはMicrosoft Update CatalogでKB5025224を検索することで実現されました。
KB5025224のMicrosoft Updateカタログ成果
具体的には、累積更新ファイル
windows10.0-kb5025224-x64_3522b1b562ed44bc949541dd1c7d08e1e967d925.msu
for Windows 11 for x86-based Systemsが取得されました。
7-Zip抽出ツールは、MSUファイルとそれに含まれるCABファイルの内容を解凍するために使用されました。結果のフォルダーにある全ファイルにわたって「msmq」という文字列を検索すると、次のようなコンテナ・インデックス・ファイルが得られました。
Windows11.0-KB5025239-x64/express.psf.cix.xmlには
以下の項目が含まれます。
<File id="21296" name="amd64_microsoft-windows-msmq-queuemanager-core_31bf3856ad364e35_10.0.22621.1555_none_5f975f8fdb349517\f\mqqm.dll" length="34811" time="133245266510000000" attr="128">
このエントリーは
MQQM.dll1.00
パッチを適用したMQQM.dllファイルとパッチを適用していないMQQM.dllファイル間のBinDiffの成果
Hex-Rays Decompilerを使用してこれらの関数を分析すると、Microsoftセキュリティ研究センター(MSRC)の脆弱性識別子を参照していると思われる詳細な関数名を含むパッチ適用バージョンのシンボルが明らかになりました。
MQQM.dll内の主要な機能シンボル
詳しく調査すると、MSRCケース番号を含むシンボルは脆弱性修正の主要な機能フラグに対応していることがわかります。主要な機能フラグは、Windowsのコンポーネントであり、様々な機能や実験を切り替えるものです。ここで発見された「主要な機能」は、実際には脆弱性に対する修正プログラムです。この主要な機能が有効になっている場合は、修正プログラムが適用された安全なコード・パスが実行されます。そうでない場合は、修正プログラムが適用されていない元のコード・パスが実行されます。
脆弱性修正の機能フラグを示すデコンパイルされたコード・スニペット
ユーザー・コンポーネントとカーネル・コンポーネントの両方にあるCVE-2023-21554を構成する脆弱性それぞれに、機能フラグ・チェックが存在しました。セキュリティアップデートパッケージに含まれる残りのバイナリの分析では、2023年4月のパッチチューズデーで修正されたほとんどまたはすべての脆弱性に対して主要な機能が導入されていたことが明らかになりました。
これらのフラグを相互参照することで、パッチが適用されたコードの場所を迅速に特定することができました。特に、MSRC76146_MSMQ_OOBRWFixes flagに関連した内の変更に最も利点が含まれているようでした。
この主要な機能フラグのチェックは、関数内で8回発生します。そのようなチェック項目の1つを以下に示します。
修正プログラムを示すデコンパイルされたコード・スニペット
上記のデコンパイルされたコード・スニペットでは、パッチ適用されたコード・パスは、
GetNextSectionPtrSafe関数のデコンパイルスニペット
この主要な機能などのすべての発生を調査した結果、CVE-2023-21554は、さまざまなMSMQメッセージセクションタイプのサイズを十分にチェックしないいくつかのバグで構成されていることが判明しました。これらの脆弱性により、MSMQメッセージの終わりへのポインターを、元に割り当てられたバッファーの境界の外側に任意の32ビット・オフセットによって増分されることができます。
パケット処理の後の
メッセージ終了時の境界外メモリーアクセスチェック
脆弱性の悪用可能性を評価中に、MSMQサービスに対応するカーネル要因で追加のカーネル脆弱性が特定されました。この脆弱性は、再び主要な機能フラグ・シンボルで示され、で見られるMSRCケース76146も参照されます。
mqac.sys内の主要な機能フラグシンボル
関連するオペレーションを実行するために、カーネル・コンポーネントはMSMQパケット・バッファーのさまざまなセクションを解析します。そのような2つのインスタンスを以下に示します。
カーネル・要因mqac.sysの脆弱性修正の主要な機能フラグを示すデコンパイルされたコード・スニペット
上記の逆コンパイルされたコード・スニペットに見られるように、主要な機能flagが有効な場合、「安全な」関数
プロトコル解析に関連するコードの大部分は、ライブラリー内にあり、mqsvc.exeで参照されるコードに到達するには、クライアントから有効なMSMQリクエストが送信される必要があります。MSMQメッセージの仕様とプロトコルは、膨大で複雑です。当初、GitHub gistでMSMQサーバーをスキャンするための公開コードが見つかり、パケット処理のより詳細な調査の基礎となりました。さらに、MicrosoftのドキュメンテーションはMSMQメッセージ形式の理解にも役立ちました。
以下のデバッグセッションでわかるように、DLL内のコードは、WSARecv関数の呼び出しを使用してポート1801経由でデータを受け入れます。
0:020> bp ws2_32!wsarecv ; g
Breakpoint 0 hit
WS2_32!WSARecv:
00007ffd`651115c0 48895c2408 mov qword ptr [rsp+8],rbx ss:000000aa`f07fef30=0000000000000006
0:019> k
# Child-SP RetAddr Call Site
00 000000aa`f07fef28 00007ffd`57ba9cb0 WS2_32!WSARecv
01 000000aa`f07fef30 00007ffd`57b9683d
MQQM!NoReceivePartialBuffer+0x38
02 000000aa`f07fefb0 00007ffd`57b2078e MQQM!CWinsockConnection::ReceivePartialBuffer+0x7d
03 000000aa`f07ff010 00007ffd`57b25b28
MQQM!CSockTransport::BeginReceive+0xe6
04 000000aa`f07ff060 00007ffd`57b2d40b
MQQM!CSockTransport::NewSession+0x194
05 000000aa`f07ff3f0 00007ffd`57b2d367 MQQM!CSessionMgr::AcceptSockSession+0x6f
06 000000aa`f07ff430 00007ffd`645e26bd MQQM!AcceptIPThread+0x6a7
07 000000aa`f07ff7d0 00007ffd`6650a9f8
KERNEL32!BaseThreadInitThunk+0x1d
08 000000aa`f07ff800 00000000`00000000 ntdll!RtlUserThreadStart+0x28
0:019> r @$t0=@rdx
0:019> pt
WS2_32!WSARecv+0x19f:
00007ffd`6511175f c3 ret
0:019> dc poi(@$t0+8) LC
000001b5`880ff490 62f06110 524f494c 00000070 5a5a5a5a .a.bLIORp…ZZZZ
000001b5`880ff4a0 0073006e 00610074 0063006e 00200065 n.s.t.a.n.c.e. .
000001b5`880ff4b0 00440049 00000000 00000000 00000000 I.D………….
0:019> ba r1 poi(@$t0+8) ; g
Breakpoint 1 hit
MQQM!CBaseHeader::SectionIsValid+0x154:
00007ffd`57b53450 7462 je
00007ffd`57b53450 7462 je
この関数は、BaseHeader構造体で始まる新しいメッセージの先頭を解析します:
BaseHeaderオブジェクトの構造
受信メッセージ・フィールドは、サーバーが応答を送信する前に
MQQM.dll内のSectionIsValidのコードをディスアセンブリします
初期検証の後、プロトコル解析の大部分は
EodHeaderの処理における脆弱性を示すデコンパイル
上記のデコンパイルは、
EodHeaderセクションの合計サイズを計算するディスアセンブリ
上記のディスアセンブリーでは、
不正なMSMQメッセージの最後に書き込むときにアクセス違反が発生するWinDbgセッション
脆弱性によって得られる境界外書き込みの強度を評価するために、まず基盤となるメモリーの割り当てと管理を調査しました。
MSMQで割り当てられたバッファーにコピーされます。この関数は、以下VIA® カーネル要因を呼び出します:NtDeviceIoControl.
パケット・バッファ割り当て機能のデココンパイル
カーネル・コンポーネントは、ディスク上のファイルのマッピングされたビューから分割されたバッファーを返し、
ProcessHackerのMQSVCプロセスのメモリーレイアウトは、パケットバッファメモリーがファイルマッピングであることを示しています。
パケットバッファアドレスとランダム化されたパケットハンドルは、IOCTL経由で割り当て要求後にユーザー空間に返されます。ドライバーは、ユーザー空間からのリクエストを処理する際に、このハンドルを使用してパケット・バッファーのアドレスを計算します。
脆弱なバッファーのバック・メモリーはマッピングされたファイル内にあり、プロセスのヒープ内にありません。さらに、影響を受けるメモリーにはオブジェクトが保管されません。ファイル・マッピングに含まれる唯一のものは、MSMQメッセージの内容と、ファイル・マッピングに保管されているメタデータです。
さらに、メモリー配置に関する事前の知識がなければ、オブジェクト・ポインターを上書きするためにプロセスのヒープに対するファイル・マッピングの相対的な位置を知ることはできません。したがって、リモート・コードの実行を確実に実現するには、メモリーを予測可能な位置に形成するための何らかの方法が必要です。
正当なMSMQメッセージをターゲットに送信して実験した結果、多くのメッセージをスプレーすることで、
MQファイルマッピングに隣接して割り当てられた新しいヒープを示すMQSVCプロセスのメモリーレイアウト
ただし、これには攻撃者がターゲットの少なくとも1つのキューにメッセージを送信するためのアクセス権が必要です。そうしないと、メッセージは破棄されて上書きされ、パケットメモリーを永続的に占有しません。
実施された研究実験では、権限のないユーザーが、デフォルトで誰でもリモートでメッセージを送信できるキューを作成できることが判明しました。キューはシステム上に無期限に残ります。MSMQの最新の使用法のほとんどは、オプションのコンポーネントやアダプターとして存在しますが、Oracle、Veritas、Xeroxなどのベンダーは、MSMQをコアの依存関係として利用するエンタープライズ・ソフトウェアを提供しています。したがって、攻撃シナリオは非現実的ではありません。
out-of-bound memory accessの脆弱性は、悪意のあるクライアントがパケットに割り当てられたメモリーのBoundaryを超えてOnDiskExtensionHeader
オフセットへの値の書き込みを可能にする脆弱性を評価する際には、
次のような特性を考慮する必要があります。
QueueJumper脆弱性の場合、割り当てられたバッファのOOB書き込みへのオフセットは、攻撃者が制御するデータを使用して計算されるため、制御可能です。ただし、OOB書き込みの内容の制御は限られています。
ここでは、Addressヘッダーが記述されるパケット・メッセージの末尾への破損したポインターを表します。発生する一連のオペレーションは次のとおりです。
0x000000000000000C
の書き込み先Address0x00000000Address+0x2
に書き込まれます0x0000000000000000Address+0x12
に書き込まれます0x0000000000000000
は、Address+0x1A
に書き込まれます0x00000094
は、Address+0xE
に書き込まれます0x0000Address+0x220x0000
は、Address+0x62
に書き込まれますmemcpy(Address+0xA6, Source, Source->AddressLength+0x08)
Sourcememcpy構造体は、特定のタイプ(例:NetBIOS)の単一のトランスポート所在地を定義します。定義は以下の通りです。
typedef struct _TA_ADDRESS {
USHORT AddressLength;;
USHORT AddressLength;;
UCHAR Address[1];;
} TA_ADDRESS, *PTA_ADDRESS;
指定したAddressTypeのアドレスのバイト数を指定します。
トランスポートのアドレスのタイプを指定します。
トランスポートのアドレスを含む可変サイズの配列を指定します。
以下のデバッグ・セッションでは、TA_ADDRESS
RAX=0000014E1D110180 RBX=0000014E1D110180 rcx=0000014E1D110184
RDX=0000014E1ce86B10 rsi=0000014E1d110001 RDI=0000000545d7fa50
RIP=00007FF843208074 RSP=00000000545d7F940 RBP=0000000545d7F980
r8=000000000000000c r9=0000000000000002 r10=0000000000000000
r11=0000000545d7f938 r12=0000000000000002 r13=0000000000001000
r14=0000014e1ce86b10 r15=000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
MQQM!CQmPacket::CQmPacket+0x89c:
00007ff8`43208074 e8a5780a00 call MQQM!memcpy (00007ff8`432af91e)
0:017> dc rcx
0000014e`1d110184 00010004 00000000 8620a8c0 00000000 ………. …..
上記のTA_ADDRESS、タイプは1です。各バイトをIPv4オケットに変換すると、所在地が生成されます。これはクライアントのIPv4ソース所在地です。
このシナリオでは、アドレスを介して、メモリー内のパケット割り当てから制御されたオフセットまで、攻撃者が管理するデータを書き込むことができます。これは非常に制限的で、おそらく非現実的ですが、理論的には、パケット・オフセットを減少させる際に、さまざまなアドレスから複数のリクエストを行うことで、メモリーに書き込まれる値を正確に制御するために使用できます。
盲目的なリモート・コード実行を実現するためには多くの制限があるため、情報漏洩などの代替攻撃シナリオが調査されました。
考慮された攻撃の1つは、特定のメッセージ・セクションへのポインターを破損して境界外のメモリーを指定し、後でメッセージを取得して隣接するメモリーを読み取ることでした。
ただし、各セクションの解析の開始時に現在のセクションの位置が確認されるため、解析ロジックをだまして境界外にあるデータ・セクションを解析することはできません。
もう一つの攻撃シナリオは、別のメッセージのOnDiskExtensionHeaderを上書きして、受信したメッセージの出所をメッセージの受信者にだますことである。同様に、攻撃者がアクセスできないキュー宛のメッセージの内容を上書きすることも可能です。メッセージの特定の内容を上書きするには、攻撃者はメッセージのサイズなどの情報を持っている必要があります。
範囲外のデータ・アクセスの影響を受けやすいSoapHeaderという名前のオプションのセクションがあり、次の形式になります。
SoapHeaderパケット形式
MSMQパケット・データの保管に使用されるメモリーが、バッファーが解放されたときに適切にクリアされていないことが確認されました。その結果は、以前のMSMQパケットよりもサイズが小さいMSMQパケットを送信すると、メモリーレイアウトには、現在のパケットのデータが含まれ、その後に以前に処理されたMSMQパケットのデータが続きます。
書き込み内容の制御が限られ、制御されない書き込みが隣接メモリーを破損させること、さらにメッセージレイアウトの知識不足がエクスプロイテーションの大きな障害となりました。
分析時には、MSMQメッセージ・パケットの初期処理に関連するユーザー・スペースの脆弱性のみが、悪用可能性について評価されました。カーネル・バリアントの脆弱性を分析すると、さらなるエクスプロイテーションAvenue®が明らかになる可能性があります。たとえば、不正な形式のヘッダーを含めると、ファイルのカーネル・メモリー・マッピングに書き込みプリミティブが取得される可能性があります。場合によっては、ユーザー空間処理のチェックにより、カーネルが一部の後処理オペレーションを実行できなくなる場合があります。
さらに、カーネルの亜種の場合、攻撃者はメッセージの内容をあまり直接制御できず、カーネル・プール(ヒープ)内ではなく、ユーザー空間と同じファイル・マッピングを操作します。KASLRなどの緩和策も、リモートからのエクスプロイテーションに対しては克服が困難な障害をもたらします。
パッチが適用されたコードの実装により、リモート・クライアントは、システムが更新を適用したかどうかを安全に判断できます。これは、脆弱性をトリガーするMSMQメッセージを送信することで実現できますが、実際には境界外アクセスを引き起こさず、サービス・プロセスをクラッシュさせることはありません。HTTPフラグがメッセージのUserHeaderに設定されている場合、SRMPEnvelopeHeaderが想定されます。パッチでは、2を掛けたフィールドで整数オーバーフローのチェックを実行します。この長さは、乗算の成果がデータの実際のサイズに等しい小さな数までオーバーフローするように設定できます。
SRMPEnvelopeHeader Data機能の整数オーバーフロー・チェックを示すデコンパイルされたコード・スニペット
ターゲットにパッチが適用されると、その脆弱性により例外が生成され、サーバーがメッセージの処理を中止するため、応答はクライアントに送り返されません。パッチが適用されていない場合、サーバーはメッセージを通常処理し、応答を送信します。
脆弱なバッファーのメモリーをバックアップする場所によっては、ターゲット上の任意のキューにメッセージを送信できる攻撃者にとって、リモート・コード実行がより実現可能になると思われます。このシナリオでは、隣接するヒープが割り当てられるようにクリーンアップできます。書き込みコンテンツの制御が非常に限られているため、リモートのエクスプロイテーションに対する障害となります。ASLRのような緩和策のために、一定の情報漏洩も許容する必要があります。定数(0xC, 0x0
最終的に私たちは、QueueJumperのリモートエクスプロイテーションは不可能ではない可能性があるものの、前述の要件により実現が困難になっていると結論づけています。このブログ記事は、非常に複雑なメッセージ仕様、サービス、プロトコルについての表面を少しかじったに過ぎません。ユーザー空間とカーネル・コンポーネント・オペレーションの両方について、さらなる研究が必要です。