ksmbd streams_xattr OOB write → local LPE (CVE-2025-37947)

Reading time: 9 minutes

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をサポートする

このページは、ksmbd の streams 処理における決定論的な out-of-bounds write を記録しており、標準的なカーネルヒーププリミティブ (msg_msg + pipe_buffer) を用いて KASLR、SMEP、SMAP を回避し、Ubuntu 22.04 LTS (5.15.0-153-generic) 上で信頼性のある Linux カーネル権限昇格を可能にします。

  • 影響を受けるコンポーネント: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
  • プリミティブ: page-overflow OOB write past a 0x10000-byte kvmalloc() buffer
  • 前提条件: ksmbd が vfs streams_xattr を使用する認証済みかつ書き込み可能な共有で実行されていること

Example smb.conf

ini
[share]
path = /share
vfs objects = streams_xattr
writeable = yes

根本原因(割り当てはクランプされているが、memcpy がクランプされていないオフセットで実行される)

  • 関数は size = *pos + count を計算し、超過した場合に size を XATTR_SIZE_MAX (0x10000) にクランプし、count を (*pos + count) - 0x10000 に再計算しますが、それでも memcpy(&stream_buf[*pos], buf, count) を 0x10000 バイトのバッファに対して実行します。もし *pos ≥ 0x10000 なら、コピー先ポインタは既に割り当て外になっており、count バイトの OOB 書き込みが発生します。
脆弱な関数の抜粋 (ksmbd_vfs_stream_write)
c
// https://elixir.bootlin.com/linux/v5.15/source/fs/ksmbd/vfs.c#L411
static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count)
{
char *stream_buf = NULL, *wbuf;
size_t size;
...
size = *pos + count;
if (size > XATTR_SIZE_MAX) {             // [1] clamp allocation, but...
size = XATTR_SIZE_MAX;
count = (*pos + count) - XATTR_SIZE_MAX; // [1.1] ...recompute count
}
wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); // [2] alloc 0x10000
stream_buf = wbuf;
memcpy(&stream_buf[*pos], buf, count);         // [3] OOB when *pos >= 0x10000
...
kvfree(stream_buf);
return err;
}

Offset steering and OOB length

  • 例: file offset (pos) を 0x10018、original length (count) を 8 に設定します。clamping 後、count' = (0x10018 + 8) - 0x10000 = 0x20 になりますが、memcpy は stream_buf[0x10018] から 32 バイトを書き込み、つまり 16 ページ割り当ての範囲を 0x18 バイト超えます。

Triggering the bug via SMB streams write

  • 同じ authenticated SMB connection を使い、share 上のファイルを開いて named stream (streams_xattr) への write を発行します。file_offset ≥ 0x10000 に小さな length を設定すると、サイズを制御可能な決定論的な OOB write を生成できます。
  • libsmb2 を使って SMB2/3 上でそのような write を認証・作成できます。

Minimal reachability (concept)

c
// Pseudocode: send SMB streams write with pos=0x0000010018ULL, len=8
smb2_session_login(...);
smb2_open("\\\\host\\share\\file:stream", ...);
smb2_pwrite(fd, payload, 8, 0x0000010018ULL); // yields 32-byte OOB

Allocator behavior and why page shaping is required

  • kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) requests an order-4 (16 contiguous pages) allocation from the buddy allocator when size > KMALLOC_MAX_CACHE_SIZE. This is not a SLUB cache object.
  • memcpy occurs immediately after allocation; post-allocation spraying is ineffective. You must pre-groom physical memory so that a chosen target lies immediately after the allocated 16-page block.
  • On Ubuntu, GFP_KERNEL often pulls from the Unmovable migrate type in zone Normal. Exhaust order-3 and order-4 freelists to force the allocator to split an order-5 block into an adjacent order-4 + order-3 pair, then park an order-3 slab (kmalloc-cg-4k) directly after the stream buffer.

Practical page shaping strategy

  • Spray ~1000–2000 msg_msg objects of ~4096 bytes (fits kmalloc-cg-4k) to populate order-3 slabs.
  • Receive some messages to punch holes and encourage adjacency.
  • Trigger the ksmbd OOB repeatedly until the order-4 stream buffer lands immediately before a msg_msg slab. Use eBPF tracing to confirm addresses and alignment if available.

Useful observability

bash
# Check per-order freelists and migrate types
sudo cat /proc/pagetypeinfo | sed -n '/Node 0, zone  Normal/,/Node/p'
# Example tracer (see reference repo) to log kvmalloc addresses/sizes
sudo ./bpf-tracer.sh

エクスプロイト計画 (msg_msg + pipe_buffer), adapted from CVE-2021-22555

  1. 多数の System V msg_msg primary/secondary messages を spray(4KiB サイズ、kmalloc-cg-4k に収まる)。
  2. ksmbd の OOB をトリガーして primary message の next pointer を破損させ、2つの primaries が1つの secondary を共有するようにする。
  3. キューにタグを付け、msgrcv(MSG_COPY) でスキャンしてタグが不一致のペアを検出する。
  4. 実際の secondary を free して UAF を作り、UNIX sockets 経由で制御したデータを reclaim(fake msg_msg を作成)する。
  5. copy_msg の m_ts over-read を悪用して mlist.next/mlist.prev を取得し、カーネルヒープポインタを leak(SMAP bypass)。
  6. sk_buff spray を使い、有効なリンクを持つ一貫した fake msg_msg を再構築して通常どおり free し、状態を安定化させる。
  7. UAF を struct pipe_buffer オブジェクトで reclaim;anon_pipe_buf_ops を leak して kernel base を算出(KASLR を破る)。
  8. release がスタックピボット/ROP ガジェットを指すように偽の pipe_buf_operations を spray;パイプを close して実行し、root を取得する。

Bypasses and notes

  • KASLR: anon_pipe_buf_ops を leak して base (kbase_addr) とガジェットアドレスを算出。
  • SMEP/SMAP: pipe_buf_operations->release のフローでカーネルコンテキストで ROP を実行;prepare_kernel_cred/commit_creds チェーン等で userspace 参照を避けるまでユーザ空間参照を行わない。
  • Hardened usercopy: このページの page overflow プリミティブには該当せず;破壊対象は non-usercopy フィールド。

Reliability

  • 隣接性が得られれば高い;稀にミスや panic (<10%) が発生。spray/free の回数を調整すると安定性が向上。特定の衝突を誘発するためにポインタの下位2バイトを上書きする手法が有効と報告されている(例: オーバーラップ領域に 0x0000_0000_0000_0500 パターンを書き込む)。

Key parameters to tune

  • msg_msg spray の数とホールパターン
  • OOB オフセット (pos) と結果としての OOB 長 (count')
  • 各ステージでの UNIX socket、sk_buff、pipe_buffer の spray 数

Mitigations and reachability

  • Fix: 両方の割り当てサイズと destination/length を clamp するか、memcpy を割り当てサイズ以内に制限すること;upstream パッチは CVE-2025-37947 として追跡済み。
  • リモート利用には信頼できる infoleak とリモートヒープグルーミングが追加で必要;この解説はローカル LPE に焦点を当てている。

References PoC and tooling

  • libsmb2 for SMB auth and streams writes
  • eBPF tracer script を使って kvmalloc アドレスをログ取りしヒストグラムで割り当てを確認(例: grep 4048 out-4096.txt)
  • Minimal reachability PoC とフルローカルエクスプロイトは公開されている(References を参照)

References

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をサポートする