
CVE-2023-32233(Linuxカーネルnf_tablesのuse-after-free脆弱性で、ローカル権限昇格を可能にする)の再現と根本原因分析。PoCおよびエクスプロイトに関する注記付き。
このリポジトリについて: これは CVE-2023-32233 の実践的な再現と調査です。これは Linux カーネルの Netfilter nf_tables サブシステムにおける use-after-free であり、ローカル権限昇格を可能にし、 2023年5月に Patryk Sondej と Piotr Krysiuk によって公開されました。
クレジット: PoC (exploit.c) と元の解説は
@Liuk3r によるものです。このリポジトリでの私の作業は、
自身のラボ (Ubuntu 23.04、カーネル 6.2.0-20) でエクスプロイトを再現し、
以下に根本原因とエクスプロイトチェーンを文書化することです。すべてのテストは
専用のラボマシンで実施しました。
nf_tables は設定更新をアトミックなバッチとして処理します。同じバッチ内の 以前の操作による状態変化に対する各操作の検証が不十分です。具体的には:
nft_set に対する lookup 式を含む
nft_rule から開始します。NFT_MSG_DELRULE — ルールを削除し、これにより暗黙的に
lookup 式と匿名 nft_set が削除されます;NFT_MSG_DELSETELEM — すでに削除された匿名セットの要素を
削除します。nf_tables_commit_release() はリソースを
nf_tables_destroy_list にキューイングし、後で
nf_tables_trans_destroy_work() によって処理されます:
まず nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() が
nft_set を解放します;
次に、NFT_MSG_DELSETELEM に対して nf_tables_set_elem_destroy() が
nft_set_elem_ext() を呼び出し、これが解放済みの nft_set を
逆参照します:
set->ops->elemsize が破壊されている場合、攻撃者が選択したメモリ位置が
nft_set_ext として解釈されます — これが他のすべての基盤となるプリミティブです。
nf_tables_trans_destroy_work() との競合に勝つ: 制御された遅延として
大きなセット破棄操作を挿入し、他の CPU をビジー状態にピン留めし、
解放された nft_set チャンクを同じ CPU 上で異なる型の
nft_set (異なる elemsize) で再割り当てする → 型混同。nft_set_ext ヘッダーを範囲外のオフセットで作成し、
nf_tables_set_elem_destroy() が隣接するチャンクを破棄すべき
nft_expr のリストとして走査するようにします。NFTA_LOG_PREFIX を持つ nft_log 式をスプレーします;
nft_log_destroy() が priv->prefix を解放し、kmalloc-{8..192} に
重複する割り当てプリミティブを与えます (最初は NULL バイト制限付き)。nft_object->udata で再利用して、ダングリング読み取りに対する
NULL バイト制限を解除します。注目すべきは、選択されたプリミティブが CFI がブロックするものを回避していることです — どのステップでも間接呼び出しハイジャックは不要です。
SLAB_FREELIST_HARDENED)、INIT_ON_FREE、および CPU ごとの slab 再利用
制限はすべてステップ 1 を攻撃します; CFI (存在する場合) はステップ 3 以降を
制約します。nf_tables バッチ netlink メッセージに対する audit ルール; スタック内の
nft_set_elem_ext / nf_tables_trans_destroy_work のクラッシュトリアージ。nftables は CAP_NET_ADMIN を必要としますが、
非特権ユーザー名前空間がそれを付与します — これが
kernel.unprivileged_userns_clone=0 が意味のある強化ノブである理由です。Ubuntu 23.04 (Lunar Lobster)、カーネル 6.2.0-20-generic でテスト済み。
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
組み込みプロファイルは Ubuntu 23.04 バイナリカーネル
(linux-image-6.2.0-20-generic 6.2.0-20.20) を対象としています。他のカーネルを
テストするには、シンボルを profile ファイルに抽出します:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
nft_counter_destroy() のマシンコードレイアウトはコンパイラとオプションに
よって異なります; 完全なパラメータリファレンス
(nft_counter_destroy_call_offset/mask/check) とレース調整ノブ
(race_lead_sleep など) については ORIGIN.md を参照してください。
報告されている成功率はアイドル状態のベアメタル Intel システムで ≥80% です;
一部のマイクロアーキテクチャ (例: Alder Lake) では追加の調整が必要です。
PoC は成功した実行後、破壊されたメモリとともにカーネルを不安定な状態に 残します。専用の使い捨てシステムまたは VM スナップショットでのみテストして ください — 大切なデータがあるものでは決して行わないでください。
exploit.c — PoC ソース (@Liuk3r による)ORIGIN.md — 元の脆弱性とエクスプロイトの解説 (Liuk3r による)README.md — このファイル: 再現 + 私の分析ノート研究は隔離されたラボ環境で防御/教育目的で実施されました。
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_dynsetnft_counter — nft_counter_ops をリーク → nf_tables.ko の
ベース (モジュールの KASLR を無効化);nft_quota — consumed ポインタが任意読み取り
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
と任意書き込み (nft_overquota() がループバックトラフィックで
skb->len を *priv->consumed に加算) を与えます。modprobe_path を上書き ("/sbin/modprobe" → "//tmp/modprobe") →
攻撃者が制御する内容で root プロセスを実行。