Share
Embed
Transcript
53 slides
-
WHOAMI Rona - 筑波大学 WORD編集部 - 趣味: CTF (pwn) -
好き: kernel空間、猫 2/53 -
1. Intro - 以下の環境で一般ユーザーがsudoなしでrootになれる - RedHad Enterprise Linuxのデフォルトインストール - rootless
Docker/podmanが入ったLinux 5/53 -
2. Prerequisite - IPSetとは? → netfilterサブシステムでIPをまとめて管理する方法 - 利点: 対象ipをiptablesなどのルールをいちいち変えずに編集できる ipset
create blocklist hash:ip ipset add blocklist 192.0.2.68 ipset add blocklist 192.0.2.70 iptables -A INPUT -m set --match-set blocklist src -j DROP ipsetの例 8/53 -
2. Prerequisite ipset create blocklist hash:ip ipset add blocklist 192.0.2.68
ipset add blocklist 192.0.2.70 iptables -A INPUT -m set --match-set blocklist src -j DROP - IPSetとは? → netfilterサブシステムでIPをまとめて管理する方法 - 利点: 対象ipをiptablesなどのルールをいちいち変えずに編集できる このサブシステムに脆弱性があった ipsetの例 9/53 -
htable 0 1 2 3 401 402 403 404 region
0 region 1 hbucket used size pos value [] - htableはhash table (入力データのhash を使って bucket を選ぶ table) - hbucketへのポインタを持つ - hbucketは実データを保管するbucket 2. Prerequisite | IPSetの構造 10/53 -
2. Prerequisite | hbucketの構造 rcu_head rcu u8 size, pos long
used (bitmap) u32 ip ulong timeout u32 skbmark, skbmarkmask hbucket u16 skbprio, skbqueue ... ヘッダー部 データ部 (何個も続く) 11/53 -
- hbucketに大きくなってほしくないのでhtableをregionに分割 - hashの衝突が多くなってきたらregionを増やして多くのhbucketに分散 - 1regionは0x400個 2. Prerequisite | IPSetの構造
htable 0 1 2 3 401 402 403 404 region 0 region 1 801 802 803 804 region 2 12/53 -
1. 入力値のjhashを取って、tableのサイズ分だけにtruncate 入力 value jhash & (tablesize -1) 例: 192.168.1.1
例: 0x12318e42 例: 0x242 (region数 == 1) 例: 0xe42 (region数== 4) 2. Prerequisite | IPSet addの実装 13/53 -
2. htableのうち、先程計算したindex番目のhbucketを編集 hbucket used pos/size 192.168.1.68 192.168.1.1 0 1 2
... 0xe41 0xe42 htable 2. Prerequisite | IPSet addの実装 14/53 -
3. add時にtimeout (==有効期限)を設定できる hbucket used pos/size 192.168.100.98 192.168.1.1 / 1m
0 1 2 ... 0xe41 0xe42 htable 2. Prerequisite | IPSet addの実装 15/53 -
1. region毎にtimeoutしたエントリの存在を確認/存在したら削除 2. Prerequisite | IPSet GCの実装 hbucket used pos/size
192.168.100.98 192.168.1.1 / 1m 0 1 2 ... 0xe41 0xe42 htable 16/53 -
2. Prerequisite | IPSet GCの実装 hbucket used pos/size 192.168.100.98 削除
0 1 2 ... 0xe41 0xe42 htable 1. region毎にtimeoutしたエントリが存在確認/存在したら削除 17/53 -
3. Vulnerability | lockの取り方 - Kernelはマルチスレッドなので、適切なlockが必要 - region毎にhregion[n]をlockする方針 1region毎にlockを取ってGC bucket
index % region数でlockを取る 0 GC側 Add 側 htable htable 0x401 1 2 0x403 0x402 例: hregion[0]のlockを取ってGCするもの 例: hregion[0]のlockを取ってaddするもの (region数 == 2とする) 1 0x402 0x401 0x403 0 2 18/53 -
3. Vulnerability | lockの取り方 - Kernelはマルチスレッドなので、適切なlockが必要 - region毎にhregion[n]をlockする方針 → add側がlockの取り方をミスってる!!
1region毎にlockを取ってGC bucket index % region数でlockを取る 0 GC側 Add 側 htable htable 0x401 1 2 0x403 0x402 例: hregion[0]のlockを取ってGCするもの 例: hregion[0]のlockを取ってaddするもの (region数 == 2とする) 0x402 0x401 0x403 1 0 2 19/53 -
3. Vulnerability | パッチの意味 - % が / になっている →
add側のlockの計算マクロがこれ https://lore.kernel.org/linux-cve-announce/2025052902-CVE-2025-37997-2c6c@gregkh/T/#u 20/53 -
3. Vulnerability | lockの不整合・具体例 0 1 2 ... 0x401 0x402
htable 例: region数==2のとき GC Thread - region0のGCをしている - hregion[0]をlockする add Thread - index 1にエントリを追加し ようとしている - hregion[1]をlockする 21/53 -
0 1 2 ... 0x401 0x402 htable 例: region数==2のとき GC
Thread - region0のGCをしている - hregion[0]をlockする add Thread - index 1にエントリを追加し ようとしている - hregion[1]をlockする - (index % region数)をlock 3. Vulnerability | lockの不整合・具体例 22/53 -
0 1 2 ... 0x401 0x402 htable 例: region数==2のとき GC
Thread - region0のGCをしている - hregion[0]をlockする add Thread - index 1にエントリを追加し ようとしている - hregion[1]をlockする - (index % region数)をlock index==1に対して2スレ ッドが同時に操作 3. Vulnerability | lockの不整合・具体例 23/53 -
4. Exploit | bucketの構造 rcu_head rcu u8 size, pos long
used u32 ip ulong timeout u32 skbmark, skbmarkmask hbucket u16 skbprio, skbqueue ... ヘッダー部 データ部 (何個も続く) 24/53 -
1. regionのはじめから最後までを loop 2. 各regionにおいて、bucketのすべ てのエントリをloop 3. usedが立ってないエントリと expireしたエントリをd++で数える (expireしたものはusedのbitを落と
す) mtype_gc_doの実装 4. Exploit | GCの実装 28/53 -
1. regionのはじめから最後までをloop 2. 各regionにおいて、bucketのすべてのエントリ をloop 3. usedが立ってないエントリとexpireしたエント リをd++で数える (expireしたものはusedのbitを 落とす)
4. dがAHASH_INIT_SIZEが2を超えていたら mtype_gc_doの実装 4. Exploit | GCの実装 29/53 -
4. Exploit | GCの実装 1. regionのはじめから最後までをloop 2. 各regionにおいて、bucketのすべてのエントリ をloop 3.
usedが立ってないエントリとexpireしたエント リをd++で数える (expireしたものはusedのbitを 落とす) 4. dがAHASH_INIT_SIZE(2)を超えていたら 5. (最大エントリ数 - 2) * エントリサイズの領域を alloc mtype_gc_doの実装 30/53 -
4. Exploit | GCの実装 1. regionのはじめから最後までをloop 2. 各regionにおいて、bucketのすべてのエントリ をloop 3.
usedが立ってないエントリとexpireしたエント リをd++で数える (expireしたものはusedのbitを 落とす) 4. dがAHASH_INIT_SIZE(2)を超えていたら 5. (最大エントリ数 - 2) * エントリサイズの領域を alloc 6. 元の領域からusedのbitが立っているエントリ だけコピー mtype_gc_doの実装 31/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 192.0.2.194 / 0s 192.0.2.253 / 0s 1. GCがhregion[0]のlockを取ってregion0のGC を開始 具体例 32/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 192.0.2.194 / 0s 192.0.2.253 / 0s 1. GCがhregion[0]のlockを取ってregion0のGC を開始 2. GCがタイムアウトしたエントリのused bitを 落とす 具体例 33/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 203.0.113.1 / 1h 192.0.2.253 / 0s 1. GCがhregion[0]のlockを取ってregion0のGC を開始 2. GCがタイムアウトしたエントリのused bitを 落とす 3. 別スレッドでこのhbucketに対するaddが実行 され、エントリが挿入される (used bitが再度立 つ) 具体例 34/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 203.0.113.1 / 1h 192.0.2.253 / 0s rcu_head rcu u8 size, pos new hbucket 1 2 3 4 0 long used 4. GCは2要素分少なく した領域を新たにalloc 35/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 203.0.113.1 / 1h 192.0.2.253 / 0s rcu_head rcu u8 size, pos new hbucket 1 2 3 4 0 long used 4. GCは2要素分少なく した領域を新たにalloc 5. used bitが立ってい るエントリを1つずつ コピー 192.0.2.1 / 1h 0 36/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 203.0.113.1 / 1h 192.0.2.253 / 0s rcu_head rcu u8 size, pos new hbucket 1 2 3 4 0 long used 4. GCは2要素分少なく した領域を新たにalloc 5. used bitが立ってい るエントリを1つずつ コピー 192.0.2.1 / 1h 1 0 192.0.2.68 / 1h 37/53 -
4. Exploit | GCとaddのrace condition rcu_head rcu u8 size, pos
192.0.2.1 / 1h 192.0.2.68 / 1h id==1のhbucket 1 2 3 4 0 long used 203.0.113.1 / 1h 192.0.2.253 / 0s rcu_head rcu u8 size, pos new hbucket 1 2 3 4 0 long used 4. GCは2要素分少なく した領域を新たにalloc 5. used bitが立ってい るエントリを1つずつ コピー 192.0.2.1 / 1h 1 0 192.0.2.68 / 1h 2 203.0.113.1 / 1h 範囲外書き込み!! BoF (OOB write)!! 38/53 -
4. Exploit | bucketの構造 rcu_head rcu u8 size, pos u32
ip ulong timeout u32 skbmark, skbmarkmask long used u16 skbprio, skbqueue もし範囲外書き込みの先に別のhbucketがいた ら? 赤文字のところには任意のデータを入れられる → 別のhbucketを(自認)デカsizeに書き換えられ る 39/53 -
4. Exploit | bucketの構造 rcu_head rcu u8 size, pos u32
ip ulong timeout u32 skbmark, skbmarkmask long used u16 skbprio, skbqueue もし範囲外書き込みの先に別のhbucketがいたら? 赤文字のところには任意のデータを入れられる → 別のhbucketを(自認)デカsizeに書き換えられる → 巨大な範囲外書き込みができる 40/53 -
4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?
→ 仮想アドレスと物理アドレスを対応付ける仕組み https://www.coins.tsukuba.ac.jp/~yas/coins/os2-2025/2026-01-15/index.html 42/53 -
4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?
→ 仮想アドレスと物理アドレスを対応付ける仕組み → pagetableを書き換えてある仮想アドレスを任意の物理アドレスに向けるのがdirty pagetable https://www.coins.tsukuba.ac.jp/~yas/coins/os2-2025/2026-01-15/index.html 43/53 -
4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?
→ 仮想アドレスと物理アドレスを対応付ける仕組み → pagetableを書き換えてある仮想アドレスを任意の物理アドレスに向けるのがdirty pagetable 任意の物理メモリをユーザーランドからいじれるようになる → カーネルも物理メモリに乗っていて物理メモリの世界にRWXなどという概念はない 44/53 -
4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?
→ 仮想アドレスと物理アドレスを対応付ける仕組み → pagetableを書き換えてある仮想アドレスを任意の物理アドレスに向けるのがdirty pagetable 任意の物理メモリをユーザーランドからいじれるようになる → カーネルも物理メモリに乗っていて物理メモリの世界にRWXなどという概念はない → 何でもできる (R-Xな領域の上書きも!) 45/53 -
4. Exploit | dirty pagetable 書き込み先: core_pattern - crashしたときにcoredumpを生成するためにroot権限でどんなことをすればいいか書かれ ている
- |から始まると、そのファイルを実行する 46/53 -
4. Exploit | dirty pagetable 書き込み先: core_pattern - crashしたときにコアダンプを生成するためにroot権限で何をすべきか書かれている -
|から始まると、そのファイルを実行する - 例えば |/home/rona/lpe に書き換えるとroot権限で何でも実行させられる (== 権限昇格) 47/53 -
4. Exploit | dirty pagetable 権限昇格手法 1. 範囲外書き込みで到達可能な範囲にpagetableが置かれるように調整 2. 範囲外書き込みでpagetable
entryをcore_patternが置かれている物理ページに向ける 48/53 -
4. Exploit | dirty pagetable 権限昇格手法 1. 範囲外書き込みで到達可能な範囲にpagetableが置かれるように調整 2. 範囲外書き込みでpagetable
entryをcore_patternが置かれている物理ページに向ける 3. core_patternを書き換える 49/53 -
4. Exploit | dirty pagetable 権限昇格手法 1. 範囲外書き込みで到達可能な範囲にpagetableが置かれるように調整 2. 範囲外書き込みでpagetable
entryをcore_patternが置かれている物理ページに向ける 3. core_patternを書き換える 4. win! 権限昇格! 50/53 -
4. Exploit | 影響を受けるプロダクト - 一般ユーザーがipsetをいじれる環境 → unprivileged_userns_cloneが有効なLinux - RedHad
Enterprise Linuxのデフォルトインストール - rootless Docker/podmanが入ったLinux 51/53