Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Linux Kernelの1文字のミスで 権限昇格ができた話
Search
rona
March 20, 2026
Programming
2.6k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Linux Kernelの1文字のミスで 権限昇格ができた話
rona
March 20, 2026
Other Decks in Programming
See All in Programming
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
940
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
220
act2-costs.pdf
sumedhbala
0
110
act1-costs.pdf
sumedhbala
0
230
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
220
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
17
9.1k
えっ!!コードを読まずに開発を!?
hananouchi
0
210
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
340
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
150
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
210
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
1
190
Featured
See All Featured
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
630
Fireside Chat
paigeccino
42
4k
Mobile First: as difficult as doing things right
swwweet
225
10k
sira's awesome portfolio website redesign presentation
elsirapls
0
300
WCS-LA-2024
lcolladotor
0
710
The Pragmatic Product Professional
lauravandoore
37
7.4k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
550
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Making Projects Easy
brettharned
120
6.7k
Transcript
Linux Kernelの1文字のミスで 権限昇格ができた話 rona (@rqda_A) Kernel/VM探検隊@つくば No3 1/53
WHOAMI Rona - 筑波大学 WORD編集部 - 趣味: CTF (pwn) -
好き: kernel空間、猫 2/53
1. Intro - タイトルの「1文字」は 嘘 - ごめんなさい - 1<<5(32)文字に変更させてください 3/53
1. Intro - CVE-2025-37997の話 - パッチは1<<5文字の変更 https://lore.kernel.org/linux-cve-announce/2025052902-CVE-2025-37997-2c6c@gregkh/T/#u 4/53
1. Intro - 以下の環境で一般ユーザーがsudoなしでrootになれる - RedHad Enterprise Linuxのデフォルトインストール - rootless
Docker/podmanが入ったLinux 5/53
Linux Kernelの1<<5文字のミスで 権限昇格ができた話 rona (@rqda_A) Kernel/VM探検隊@つくば No3 6/53
2. Prerequisite - パッチは32文字 - % が / になっていることが大事 https://lore.kernel.org/linux-cve-announce/2025052902-CVE-2025-37997-2c6c@gregkh/T/#u
7/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 mtype_gc_doの実装 4. Exploit | GCの実装 25/53
1. regionのはじめから最後までを loop 2. 各regionにおいて、bucketのすべ てのエントリをloop mtype_gc_doの実装 4. Exploit |
GCの実装 26/53
1. regionのはじめから最後までを loop 2. 各regionにおいて、bucketのすべ てのエントリをloop 3. usedが立ってないエントリと mtype_gc_doの実装 4.
Exploit | GCの実装 27/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 41/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
5. おわり - マルチスレッドアプリケーションを安全に書くのは難しい - Linux kernelのように良くauditされたOSSにもミスは存在する 52/53
6. 宣伝 dirty pagetableと同様の攻撃をやってみたい!という方はHungry Goatsを解いて みてください! 脆弱なLinuxカーネルモジュールを利用して権限昇格をする問題 https://alpacahack.com/challenges/hungry-goats 53/53