
CVE-2024-38063 の PoC (tcpip.sys の RCE)
これは CVE-2024-38063 用の(かなり不安定な)PoCです。この脆弱性は、2024年8月13日にパッチが適用された tcpip.sys のRCEです。この脆弱性を発見・報告したのは私ではなく、Wei です。
pip3 install scapy
スクリプト内のフィールドを変更します:
iface <- 複数のアダプタがある場合、パケット送信に使用するものを選択する必要があります。例: Linuxでは "eth0"、Windowsでは "Hyper-V Virtual Ethernet Adapter"。デフォルトのインターフェースを使用する場合は空のままにしてください。ip_addr <- ターゲットシステムのIPアドレス(IPv6)num_tries と num_batches <- 送信するパケットバッチの数。多いほどヒープ破壊が多く発生し、脆弱性をトリガーする可能性が高くなります。mac_addr <- scapyがMACアドレスを見つけられないと警告しない限り、空のままにしてください。トラブルシューティングのセクションを参照してください。スクリプトを実行します:
python3 cve-2024-38063.py
この脆弱性を再現する最も簡単な方法は、ターゲットシステムで bcdedit /set debug on を実行し、マシン/VMを再起動することです。これにより、デフォルトのネットワークアダプタドライバが kdnic.sys になり、このドライバはパケットの結合(コアレス)を非常に積極的に行います。別の環境で再現を試みる場合は、送信したパケットが結合される状態にシステムを持っていく必要があります。詳細については、下のトラブルシューティングのセクションを参照してください。
技術的な詳細に興味がある場合は、Marcus によるこの優れた脆弱性の分析を読むことをお勧めします。以下に書いた内容は、本格的な技術分析というよりは要約を目的としています。
NET_BUFFER オブジェクトが含まれています。オフセット 0x30 には、パケットがどこまで解析されたかを示す現在オフセットフィールドもあります。この段階では、オフセット値は通常 0x28 であり、IPv6ヘッダーが解析されただけで他はまだ解析されていないことを示します。tcpip!Ipv6pReceiveDestinationOptions で「宛先オプション」拡張ヘッダーを処理する際、解析エラーが発生すると tcpip!IppSendErrorList が呼び出されます。この関数は、リンクリスト内の各パケットオブジェクトに対して(現在のオブジェクトから順に)tcpip!IppSendError を呼び出します。tcpip!IppSendError に副作用があります。バッファリングされたパケットデータを先頭に「巻き戻し」、現在オフセットフィールドをゼロにリセットします。0x8C)。つまり、ドライバは IppSendError で「巻き戻された」パケットであっても、リンクリスト内の他のパケットの拡張ヘッダーの解析を続行します。0x28 ではなくゼロになっています。Ipv6pReceiveFragment を使用します。この関数はフラグメント拡張ヘッダーを解析し、現在のオフセット値から 0x30 を減算してパケット内の非ヘッダーデータの長さを計算する際、パケットのオフセットフィールドが少なくとも 0x28 であると想定します。この値は、フラグメント化されたパケットを再構築するための再構築オブジェクトに格納されます。IppSendError によって巻き戻されたパケットに対して呼び出されます。オフセット値はゼロで、Ipv6pReceiveFragment の前半で 8 に増加します。非ヘッダーデータのサイズを計算すると、値はアンダーフローして 0xffd8 になります(減算は16ビットで行われます)。Ipv6pReassembleDatagram - 再構築されたパケットの出力バッファの長さを計算するために使用されます。ただし、すべての計算は32ビットで行われ、合計長が 0xFFFF を超えないかどうかの健全性チェックがあります(この場合は実際に超えます)。Ipv6pReassemblyTimeout - ここでも同じ方法で使用されます。ただし、ここでの計算は16ビットで行われ、整数オーバーフローが発生します。これにより、後でバッファにデータをコピーする際にバッファオーバーフローが発生します。Ipv6pReassemblyTimeout をトリガーするには、フラグメントの送信者が1分間非アクティブである必要があります。私たちの戦略は次のとおりです:
IppSendError をトリガーし、その後にフラグメントパケットを送信するIpv6pReceiveFragment でアンダーフローを発生させ、16ビットの大きな値を持つフラグメントデータ長の新しい再構築オブジェクトを作成するIpv6pReassemblyTimeout がトリガーされるようにするIpv6pReassemblyTimeout のバッファサイズ計算で整数オーバーフローを発生させ、ヒープベースのバッファオーバーフローをトリガーするスクリプト内のパケットは、結合される可能性を高めるために大量に送信されます。メインのペイロードは非常にシンプルです:
また、IPv6ヘッダーのホップリミットフィールドとフローラベルフィールドも手動で設定します。脆弱性によりバッファリングされたパケットデータがリセットされることを思い出してください。つまり、フラグメントパケットを処理する際、IPv6ヘッダーがフラグメントヘッダーデータとして解釈されることになります。IPv6ヘッダーのホップリミットフィールドは、フラグメントヘッダーのIDフィールドのビットの1つとして解釈されます。これを変更することで、複数の異なるフラグメントに対して脆弱性をトリガーし、複数の異なる破壊を引き起こしてクラッシュの可能性を高めます(結局これはPoCなので)。IPヘッダーのフローリミットフィールドは、フラグメントヘッダーのオフセットおよび「継続フラグ」フィールドとして解釈されます。これを 1 に設定することで、後続のヘッダーがまだあること(そのため後で Ipv6pReassemblyTimeout をトリガーできる)と、オフセットがゼロであること(このIDの最初のパケットが到着するため)を示します。
Ipv6pReassemblyTimeout で使用しているコードパスが、元のフラグメントパケットをユニキャストで送信することを必要とするため、これを使用しています。うまくいかない場合、原因として考えられるもの:
Ether(raw(sr1(IPv6(dst={your_dest_ip})/ICMPv6EchoRequest()))).src。ただし、これはうまくいかないこともありますtcpip!Ipv6pReceiveDestinationOptions -> tcpip!Ipv6pProcessOptions -> tcpip!IppSendErrorList がヒットしているか?tcpip!Ipv6pProcessOptions にブレークポイントを設定し、[rcx] が常にゼロかどうか確認する。ゼロの場合、何らかの理由でパケットが結合されていません。tcpip!Ipv6pReceiveFragment にブレークポイントを設定し、[rcx+0x30] がゼロに等しいか確認する。ゼロでない場合、何らかの理由で脆弱性のトリガーに失敗しています。