Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Linux Kernelの1文字のミスで 権限昇格ができた話

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for rona rona
March 20, 2026

Linux Kernelの1文字のミスで 権限昇格ができた話

Avatar for rona

rona

March 20, 2026
Tweet

Other Decks in Programming

Transcript

  1. 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. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 2. Prerequisite | IPSet GCの実装 hbucket used pos/size 192.168.100.98 削除

    0 1 2 ... 0xe41 0xe42 htable 1. region毎にtimeoutしたエントリが存在確認/存在したら削除 17/53
  8. 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
  9. 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
  10. 3. Vulnerability | パッチの意味 - % が / になっている →

    add側のlockの計算マクロがこれ https://lore.kernel.org/linux-cve-announce/2025052902-CVE-2025-37997-2c6c@gregkh/T/#u 20/53
  11. 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
  12. 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
  13. 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
  14. 4. Exploit | bucketの構造 rcu_head rcu u8 size, pos long

    used u32 ip ulong timeout u32 skbmark, skbmarkmask hbucket u16 skbprio, skbqueue ... ヘッダー部 データ部 (何個も続く) 24/53
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?

    → 仮想アドレスと物理アドレスを対応付ける仕組み https://www.coins.tsukuba.ac.jp/~yas/coins/os2-2025/2026-01-15/index.html 42/53
  27. 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
  28. 4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?

    → 仮想アドレスと物理アドレスを対応付ける仕組み → pagetableを書き換えてある仮想アドレスを任意の物理アドレスに向けるのがdirty pagetable 任意の物理メモリをユーザーランドからいじれるようになる → カーネルも物理メモリに乗っていて物理メモリの世界にRWXなどという概念はない 44/53
  29. 4. Exploit | dirty pagetable 範囲外書き込みを権限昇格につなげる手法 → dirty pagetable pagetableとは?

    → 仮想アドレスと物理アドレスを対応付ける仕組み → pagetableを書き換えてある仮想アドレスを任意の物理アドレスに向けるのがdirty pagetable 任意の物理メモリをユーザーランドからいじれるようになる → カーネルも物理メモリに乗っていて物理メモリの世界にRWXなどという概念はない → 何でもできる (R-Xな領域の上書きも!) 45/53
  30. 4. Exploit | dirty pagetable 書き込み先: core_pattern - crashしたときにコアダンプを生成するためにroot権限で何をすべきか書かれている -

    |から始まると、そのファイルを実行する - 例えば |/home/rona/lpe に書き換えるとroot権限で何でも実行させられる (== 権限昇格) 47/53
  31. 4. Exploit | dirty pagetable 権限昇格手法 1. 範囲外書き込みで到達可能な範囲にpagetableが置かれるように調整 2. 範囲外書き込みでpagetable

    entryをcore_patternが置かれている物理ページに向ける 3. core_patternを書き換える 4. win! 権限昇格! 50/53