
準備済みのTAP上でLinux IPv4チェックサムオフロードのバグPwnEscapeを再現し、細工したvirtioヘッダーを注入して、範囲外のフラグメントヘッダーコピーを検証するGo製プローブ。
Linux IPv4 チェックサムオフロードのバグ(PwnEscape として知られる)を検証する Go 製プローブです。あらかじめ用意した TAP にアタッチし、ホストのフラグメンタが skb_checksum_help によって書き換えられた IPv4 ヘッダを信頼するかどうかを確認します。
このプローブはパケットの注入とキャプチャを処理します。scripts/setup.sh は pwnesc ネットワーク名前空間内でラボ用インターフェース、sysctl 設定、nftables ルールを準備します。scripts/teardown.sh はラボを削除します。
ihl を読む前に部分チェックサムを完了してしまうカーネル(ここでは 6.18.54 で再現。6.18.57 で修正済み)において:
csum_start=22 を持つ virtio チェックサムヘッダが受け入れられます。その後 VID-0 タグが 2 つ(14+4+4)剥がされるため、チェックサムの書き込みは IPv4 の version/IHL バイトに着地します。0x45 から 0x4f に変わります。フラグメンタは 60 バイトのヘッダを使用します。-head 8 では、リニアヘッドは 28 バイトであるため、60 バイトのヘッダコピーは skb_headlen を 32 バイト超えて、出力フラグメントの IPv4 オプション領域まで読み取ります。これは PWN.AI の PwnEscape 解説記事で説明されている情報漏えいプリミティブを実践するものです。CONFIG_INIT_ON_ALLOC_DEFAULT_ON が有効な場合、新規に割り当てられた slab のテールルームはコピーされたバイト内でゼロとして現れることがあります。
ihl を読む前に部分チェックサムを完了するカーネルを搭載した Linux テストホストまたは VMip、nft、sysctlnf_conntrack および nf_defrag_ipv4/dev/net/tunホスト上のリポジトリディレクトリから:
go build -o poc .
sudo ./scripts/setup.sh
sudo ip netns exec pwnesc ./poc
プローブは IFF_TAP|IFF_NO_PI|IFF_VNET_HDR を指定して pwntap を開き、pwnv1 で出力をキャプチャします。pwntap はプローブ専用にしてください。nftables の ct ルールが conntrack デフラグメンテーションを有効化し、pwnv0 は MTU 200 を使用して、再アセンブルされたデータグラムを出力時に再びフラグメント化します。
ヘッダ検査が成功すると verify: first byte 0x4f ihl=15 と出力されます。その後プローブは出力フラグメント、その IPv4 オプション、およびリニアヘッドを超えてコピーされたバイトを報告します。
-head(デフォルト 8):オフセット 0 のフラグメントペイロードをバイト単位で指定。8 から 40 までの 8 の倍数。リニアヘッドを超えてコピーされる期待バイト数は 40 - head バイトです。-rest(デフォルト 400):完了フラグメントのペイロードをバイト単位で指定。最低 16。-spray(デフォルト 0):オフセット 0 のフラグメントの直前に確保する同サイズフレームの数。-spray-fill(デフォルト 0xee):それらのフレームを埋めるために使用するバイト。-page:テールに skb メタデータを含みうる page_frag ヘッドを使用。デフォルトは slab ヘッド。-skip-verify:フラグメント化データグラムの検査へ直接進む。PWN_TAP は TAP インターフェースを選択します(デフォルト pwntap)。PWN_CAP はキャプチャインターフェースを選択します(デフォルト pwnv1)。セットアップスクリプトはこれらのデフォルト名を使用します。
プローブの終了後、pwnesc 内のシェルをすべて終了し、ホストから実行します:
sudo ./scripts/teardown.sh
これにより pwnesc が、そのインターフェース、ip pwn nftables テーブル、および名前空間ローカルの sysctl 設定とともに削除されます。