AF_UNIX MSG_OOB UAF & SKB-based kernel primitives
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
TL;DR
- Linux >=6.9 introduced a flawed
manage_oob()refactor (5aa57d9f2d53) for AF_UNIXMSG_OOBhandling. Stacked zero-length SKBs bypassed the logic that clearsu->oob_skb, so a normalrecv()could free the out-of-band SKB while the pointer remained live, leading to CVE-2025-38236. - Re-triggering
recv(..., MSG_OOB)dereferences the danglingstruct sk_buff. WithMSG_PEEK, the pathunix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()becomes a stable 1-byte arbitrary kernel read; withoutMSG_PEEKthe primitive incrementsUNIXCB(oob_skb).consumedat offset0x44, i.e., adds +4 GiB to the upper dword of any 64-bit value placed at offset0x40inside the reallocated object. - By draining order-0/1 unmovable pages (page-table spray), force-freeing an SKB slab page into the buddy allocator, and reusing the physical page as a pipe buffer, the exploit forges SKB metadata in controlled memory to identify the dangling page and pivot the read primitive into
.data, vmemmap, per-CPU, and page-table regions despite usercopy hardening. - The same page can later be recycled as the top kernel-stack page of a freshly cloned thread.
CONFIG_RANDOMIZE_KSTACK_OFFSETbecomes an oracle: by probing the stack layout whilepipe_write()blocks, the attacker waits until the spilledcopy_page_from_iter()length (R14) lands at offset0x40, then fires the +4 GiB increment to corrupt the stack value. - A self-looping
skb_shinfo()->frag_listkeeps the UAF syscall spinning in kernel space until a cooperating thread stallscopy_from_iter()(viamprotect()over a VMA containing a singleMADV_DONTNEEDhole). Breaking the loop releases the increment exactly when the stack target is live, inflating thebytesargument socopy_page_from_iter()writes past the pipe buffer page into the next physical page. - By monitoring pipe-buffer PFNs and page tables with the read primitive, the attacker ensures the following page is a PTE page, converts the OOB copy into arbitrary PTE writes, and obtains unrestricted kernel read/write/execute. Chrome mitigated reachability by blocking
MSG_OOBfrom renderers (6711812), and Linux fixed the logic flaw in32ca245464e1plus introducedCONFIG_AF_UNIX_OOBto make the feature optional.
Root cause: manage_oob() assumes only one zero-length SKB
unix_stream_read_generic() expects every SKB returned by manage_oob() to have unix_skb_len() > 0. After 93c99f21db36, manage_oob() skipped the skb == u->oob_skb cleanup path whenever it first removed a zero-length SKB left behind by recv(MSG_OOB). The subsequent fix (5aa57d9f2d53) still advanced from the first zero-length SKB to skb_peek_next() without re-checking the length. With two consecutive zero-length SKBs, the function returned the second empty SKB; unix_stream_read_generic() then skipped it without calling manage_oob() again, so the true OOB SKB was dequeued and freed while u->oob_skb still pointed to it.
最小トリガーシーケンス
char byte;
int socks[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
for (int i = 0; i < 2; ++i) {
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &byte, 1, MSG_OOB);
}
send(socks[1], "A", 1, MSG_OOB); // SKB3, u->oob_skb = SKB3
recv(socks[0], &byte, 1, 0); // normal recv frees SKB3
recv(socks[0], &byte, 1, MSG_OOB); // dangling u->oob_skb
unix_stream_recv_urg() によって露呈するプリミティブ
- 1-byte arbitrary read (repeatable):
state->recv_actor()は最終的にcopy_to_user(user, skb_sourced_addr, 1)を呼びます。ダングリングした SKB が攻撃者制御下のメモリ(または pipe page のような制御されたエイリアス)に再割り当てされると、各recv(MSG_OOB | MSG_PEEK)は__check_object_size()が許可する任意のカーネルアドレスから 1 バイトをユーザ空間にコピーし、クラッシュさせずに取得できます。MSG_PEEKを維持するとダングリングポインタが保持され、無制限に読み出せます。 - Constrained write:
MSG_PEEKがクリアされていると、UNIXCB(oob_skb).consumed += 1はオフセット0x44の 32 ビットフィールドをインクリメントします。0x100 境界でアロケートされた SKB ではこれは 8 バイト整列の語より 4 バイト上に位置し、このプリミティブはオフセット0x40にある語を +4 GiB インクリメントする操作に変換されます。これをカーネル書き込みに変えるには、そのオフセットに敏感な 64 ビット値を配置する必要があります。
Reallocating the SKB page for arbitrary read
- Drain order-0/1 unmovable freelists: 巨大な read-only anonymous VMA をマップし、すべてのページでフォルトさせて page-table を割り当てさせます(order-0 unmovable)。ページテーブルで RAM の約 10% を埋めると、その後の
skbuff_head_cacheの割り当てが order-0 リストが枯渇した際に新しい buddy ページを引くようになります。 - Spray SKBs and isolate a slab page: 何十組もの stream socketpair を使い、各ソケットに数百個の小さなメッセージ(SKB あたり約 0x100 バイト)をキューして
skbuff_head_cacheを埋めます。選択した SKB を free してターゲットの slab ページを完全に攻撃者制御下に置き、出現した read プリミティブでそのstruct pageの refcount を監視します。 - Return the slab page to the buddy allocator: ページ上のすべてのオブジェクトを free し、さらに十分な追加割り当て/解放を行って SLUB の per-CPU partial lists と per-CPU page lists からページを押し出し、buddy freelist 上で order-1 ページにします。
- Reallocate as pipe buffer: 数百個の pipe を作成します。各 pipe は少なくとも 2 つの 0x1000 バイトのデータページを予約します(
PIPE_MIN_DEF_BUFFERS)。buddy allocator が order-1 ページを分割すると、その半分が解放した SKB ページを再利用します。どの pipe・どのオフセットがoob_skbとエイリアスしているかを特定するため、pipe ページ全体にフェイク SKB を格納してユニークなマーカーバイトを書き込み、マーカーが返されるまでrecv(MSG_OOB | MSG_PEEK)を繰り返します。 - Forge a stable SKB layout: エイリアスした pipe ページを、
data/headポインタやskb_shared_info構造が任意のカーネルアドレスを指すフェイクstruct sk_buffで埋めます。x86_64 のcopy_to_user()内では SMAP が無効になるため、カーネルポインタが判明するまでユーザモードアドレスをステージングバッファとして使えます。 - Respect usercopy hardening: コピーは
.data/.bss、vmemmap エントリ、per-CPU vmalloc 範囲、他スレッドのカーネルスタック、そして高次の folio 境界をまたがない direct-map ページに対して成功します。.textや__check_heap_object()によって拒否される特殊なキャッシュに対する読み出しは、プロセスを殺さずに単に-EFAULTを返します。
Introspecting allocators with the read primitive
- Break KASLR: 固定マッピング
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) から任意の IDT ディスクリプタを読み、既知のハンドラオフセットを引くことでカーネルベースを復元できます。 - SLUB/buddy state: グローバルな
.dataシンボルはkmem_cacheベースを明かし、vmemmap エントリは各ページの type フラグ、freelist ポインタ、所属 cache を露出します。per-CPU vmalloc セグメントを走査するとstruct kmem_cache_cpuインスタンスが見つかり、主要なキャッシュ(例:skbuff_head_cache,kmalloc-cg-192)の次の割り当てアドレスを予測可能にします。 - Page tables:
mm_structを直接読む代わりに、グローバルなpgd_list(struct ptdesc) を辿り、cpu_tlbstate.loaded_mmを使って現在のmm_structと照合します。ルートpgdが分かれば、このプリミティブで全ページテーブルを辿って pipe バッファ、ページテーブル、カーネルスタックの PFN をマッピングできます。
Recycling the SKB page as the top kernel-stack page
- 制御下にある pipe ページを再度 free し、vmemmap でその refcount が 0 に戻ることを確認します。
- 直ちに 4 枚の補助 pipe ページを割り当て、逆順に free して buddy allocator の LIFO 挙動を決定論化します。
clone()を呼んでヘルパースレッドを生成します。x86_64 のスタックは 4 ページなので、直近に free された 4 つのページがそのスタックになり、最後に free されたページ(元の SKB ページ)が高位アドレスになります。- ページテーブルウォークでヘルパースレッドの top stack PFN が再利用した SKB PFN と等しいことを確認します。
- arbitrary read を使ってスタックレイアウトを観察しつつスレッドを
pipe_write()に誘導します。CONFIG_RANDOMIZE_KSTACK_OFFSETにより syscall ごとにRSPからランダムな 0x0–0x3f0(整列済み)が引かれるため、別スレッドからのpoll()/read()と繰り返しの write を組み合わせることで、writer が望むオフセットでブロックする瞬間を検出します。運が良ければ、spill されたcopy_page_from_iter()のbytes引数(R14)は再利用ページ内のオフセット0x40に位置します。
Placing fake SKB metadata on the stack
- AF_UNIX datagram socket に対して
sendmsg()を使うと、カーネルはユーザのsockaddr_unをスタック上のsockaddr_storage(最大 108 バイト)にコピーし、ancillary data を別のスタック上バッファにコピーしてから syscall がキュー空きを待ってブロックします。これによりスタック上に精密なフェイク SKB 構造を植え付けることができます。 - コピーが完了した時点を検出するには、未マップのユーザページにある 1 バイトの control message を与えます。
____sys_sendmsg()はそれをフォルトインさせるので、mincore()をポーリングするヘルパースレッドが宛先ページが存在するかを知れます。 CONFIG_INIT_STACK_ALL_ZEROによるゼロ初期化パディングが未使用フィールドを埋め、追加書き込みなしで有効な SKB ヘッダを完成させます。
Timing the +4 GiB increment with a self-looping frag list
skb_shinfo(fakeskb)->frag_listを第二のフェイク SKB(攻撃者制御のユーザメモリに格納)を指すように偽造し、その SKB がlen = 0とnext = &selfを持つようにします。skb_walk_frags()が__skb_datagram_iter()内でこのリストを反復すると、イテレータが NULL に到達しないため実行は無限ループし、コピーループは進みません。- recv syscall をカーネル内で走らせ続けるために第二のフェイク SKB をセルフループさせておき、インクリメントを発射するタイミングで単にその二番目 SKB の
nextポインタをユーザ空間からNULLに変更します。ループが抜けるとunix_stream_recv_urg()は直ちにUNIXCB(oob_skb).consumed += 1を一度だけ実行し、再利用されたスタックページのオフセット0x40にある現在のオブジェクトに影響を与えます。
Stalling copy_from_iter() without userfaultfd
- 巨大な anonymous RW VMA をマップし、完全にフォルトさせます。
madvise(MADV_DONTNEED, hole, PAGE_SIZE)で単一ページを穴にし、そのアドレスをwrite(pipefd, user_buf, 0x3000)に使うiov_iterに入れます。- 並行して別スレッドで VMA 全体に対して
mprotect()を呼びます。syscall は mmap write lock を取りすべての PTE を走査します。pipe writer が穴に到達すると page fault ハンドラがmprotect()が保持する mmap lock を待つため、copy_from_iter()が決定論的なポイントで停止し、spill されたbytes値が再利用した SKB ページ上のスタックセグメントにあるままになります。
Turning the increment into arbitrary PTE writes
- Fire the increment:
copy_from_iter()がスタールしている間に frag ループを解放して +4 GiB インクリメントをbytes変数に当てます。 - Overflow the copy: フォールトが再開されると、
copy_page_from_iter()は現在の pipe ページに >4 GiB をコピーできると信じます。正当な 0x2000 バイト(2 つの pipe バッファ)を埋めた後、さらにもう一回のイテレーションを行い、残りのユーザデータを pipe buffer PFN の後に続く物理ページに書き込みます。 - Arrange adjacency: アロケータのテレメトリを使い、buddy allocator が対象の pipe buffer ページの直後にプロセス所有の PTE ページを配置するように仕向けます(例えば pipe ページの割り当てと新しい仮想領域へのアクセスを交互に行い、PFN が同じ 2 MiB pageblock 内で揃うまで page-table 割り当てを誘発します)。
- Overwrite page tables: ユーザデータの余分な 0x1000 バイトに望む PTE エントリをエンコードし、OOB
copy_from_iter()が隣接ページを攻撃者指定のエントリで埋めて、カーネル物理メモリの RW/RWX ユーザマッピングを付与するか既存エントリを書き換えて SMEP/SMAP を無効化します。
Mitigations / hardening ideas
- Kernel:
32ca245464e1479bfea8592b9db227fdc1641705を適用して SKB を適切に再検証する(manage_oob の修正)ことを検討し、CONFIG_AF_UNIX_OOBを厳密に必要でない限り無効化する案を検討します(5155cbcdbf03)。manage_oob()を追加の健全性チェック(例:unix_skb_len() > 0になるまでループ)で強化し、他のソケットプロトコルに対しても同様の仮定がないか監査します。 - Sandboxing: seccomp プロファイルや上位ブローカー API で
MSG_OOB/MSG_PEEKフラグをフィルタリングします(Chrome の変更6711812はレンダラ側でのMSG_OOBをブロックするようになりました)。 - Allocator defenses: SLUB freelist のランダム化強化や per-cache page coloring の強制は決定論的ページ再利用を難しくします。pipe buffer 数の制限も再割り当ての信頼性を低下させます。
- Monitoring: 高頻度の page-table 割り当てや異常な pipe 使用量をテレメトリで検出します — このエクスプロイトは大量の page table と pipe バッファを消費します。
References
- Project Zero – “From Chrome renderer code exec to kernel with MSG_OOB”
- Linux fix for CVE-2025-38236 (
manage_oobrevalidation) - Chromium CL 6711812 – block
MSG_OOBin renderers - Commit adding
CONFIG_AF_UNIX_OOBprompt
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。


