CTFするぞ

あたまよくないけどがんばります

Kernel Exploitで使える構造体集

はじめに

Kernel Exploitを勉強し初めて2ヶ月ほどが経ちました。 やっていて思うのは、カーネルランドのUAFやヒープオーバーフローなどは非常に強力だということです。 しかし、脆弱なドライバが提供する機能以外に、カーネルや他のドライバが提供する機能を利用することが多いので、多くの知識が無いと上手く攻撃できなかったり、成功確率が下がったりします。 例えばkUAFはとても便利なのですが、UAF対象のオブジェクトのサイズが固定の場合は、kmallocでサイズが一致してかつ「使える」オブジェクトを探す必要があります。 この記事では、そのような「使える」オブジェクトをまとめようと思います。 といっても私はKernel Exploitに関しては素人なので間違いが多いかもしれませんし、定石みたいなものを見逃しているかもしれません。 間違っている部分の指摘や、他に知っているオブジェクトがあれば是非コメントやTwitterなどで教えてください。

実験環境や書いたコードは以下のリポジトリにまとめます。

bitbucket.org

なお、検証にはLinux Kernel 4.19.98を使いました。

Leak/AAR/AAW/RIP制御に使える構造体一覧

構造体なんて腐るほどありますが、とにかくサイズごとに使えるものを網羅したいです。 他に知ってるのあったら教えてください。 現状kmalloc-8、kmalloc-16、kmalloc-64、kmalloc-512あたりが未開拓です。

shm_file_data

  • サイズ:0x20 (kmalloc-32)
  • base:ns, vm_opsカーネルのデータ領域を指しているのでリーク可能。
  • heap:file がヒープ領域を指しているのでリーク可能。
  • stack:リークできない。
  • RIP:たぶん取れない。
  • 確保:shmatで共有メモリをマップする。
  • 解放:shmctlとかで破棄するのかな?
  • 備考:vm_opsを書き換えてみたが、shmget等で偽のvtableの関数ポインタが呼ばれる様子は特になかった。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/ipc/shm.c#L74
0x0000: 0x0000000000000000
0x0008: 0xffffffff82292ae0
0x0010: 0xffff88800ea09700
0x0018: 0xffffffff81e15540
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800ea09700

seq_operations

  • サイズ:0x20 (kmalloc-32)
  • base:4つの関数ポインタから好きなものを使ってリーク可能。
  • heap:リークできない。
  • stack:リークできない。
  • RIP:例えば start を書き換えてreadを呼べばRIPがポン!ってなる。
  • 確保:single_openを使うファイルを開く。/proc/self/statとか。
  • 解放:closeする。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/seq_file.h#L32
0x0000: 0xffffffff811c5f70
0x0008: 0xffffffff811c5f90
0x0010: 0xffffffff811c5f80
0x0018: 0xffffffff8120c3f0
[+] kbase = 0xffffffff81000000
Press enter to continue...
[    6.801190] BUG: unable to handle kernel paging request at 00000000deadbeef

msg_msg (+user-supplied data)

  • サイズ:0x31〜0x1000 (kmalloc-64以上)
  • base:リークできない。
  • heap:nextが前にmsgsndされたメッセージを指しているのでリーク可能。前にmsgsndしたデータのサイズに対応するSLUB上のアドレスを指すので便利。
  • stack:リークできない。
  • RIP:取れない。
  • 確保:msggetしてmsgsndする。
  • 解放:msgrcvする。ただし最初にmsgsndしたものから順番に受信されるのでkfreeの順番も送信した順番になる。
  • 備考:サイズ可変で任意データを書き込めるので便利だが、最初の48バイトは構造体で使われるので書き換えられない。UAFでreadのみ可能なとき、これでヒープのアドレスをリークした後にヒープ上にデータを用意するなどが可能。ヒープスプレーにもよく使われている模様。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/msg.h#L9
0x0000: 0xffff88800e1661c0
0x0008: 0xffff88800e1661c0
0x0010: 0x0000000000000001
0x0018: 0x0000000000000020
0x0020: 0x0000000000000000
0x0028: 0xffff88800e8c7f90
0x0030: 0x4242424241414141
0x0038: 0x4444444443434343
0x0040: 0x4646464645454545
0x0048: 0x0000000000000046
[+] kheap = 0xffff88800e1661c0

subprocess_info

  • サイズ:0x60 (kmalloc-128)
  • base:work.funccall_usermodehelper_exec_workを指しているのでリーク可能。
  • heap:リーク可能だがどのSLUBのデータかは未検証。
  • stack:リークできない。
  • RIP:race condition等で、確保中にcleanup を書き換えるとRIPが取れる。(これってトリビアになりませんか?)
  • 確保:いろいろありそうだけど確認済みなのはsocket(22, AF_INET, 0);などで不明なプロトコルを指定する方法。
  • 解放:確保と同一パスで解放される。
  • 備考:データ書き込めないし使えねーと思ったが、info->cleanupを設定してからif (info->cleanup) info->cleanup(info);までに時間があるので、race conditionで高い確率でRIPを取れることに検証成功。ただし、なぜかこれでROPしてもユーザーランドに復帰後fsを使う箇所やsyscallで死ぬ。SMAPが無効ならfdをユーザーランドに書き換えてuserfaultfdと組み合わせてcleanupを上書き可能だと思う。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/umh.h#L19
0x0000: 0xffff88800f254380
0x0008: 0xffff88800f254e88
0x0010: 0xffff88800f254e88
0x0018: 0xffffffff81071380
0x0020: 0x0000000000000000
0x0028: 0xffffffff82242260
0x0030: 0xffff88800e0e3b40
0x0038: 0xffffffff82242180
0x0040: 0x0000000000000000
0x0048: 0x0000010000000006
0x0050: 0x0000000000000407
0x0058: 0x0000000000000000
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800f254380
Press enter to continue...
[    6.801190] BUG: unable to handle kernel paging request at 00000000deadbeef

cred

  • サイズ:0xa8 (kmalloc-192)
  • base:リークできなさそう。
  • heap: session_keyring等からリーク可能。対象のSLUBは未調査。
  • stack:リークできなさそう。
  • RIP:取れない。
  • 確保:forkする。
  • 解放:作ったプロセスをexitする。
  • 備考:uid, gid等を持つので、0埋めできれば権限昇格できる。ただし、検証した4.19.98ではcredはkmallocに入っていない気がする。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/cred.h#L116

file

  • サイズ:? (kmalloc-256)
  • base:f_opカーネルのデータ領域を指しているのでリーク可能。
  • heap:未検証。まぁいけるやろ。
  • stack:未検証。
  • 確保:shmgetで共有メモリを作成する。
  • 解放:shmctlで破棄する。
  • 備考:f_opを書き換えてshmctl等を呼べばRIP制御が可能。ただ、UAF後にfile構造体がなぜかoverlapしなかったので検証に失敗。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/fs.h#L891

timerfd_ctx

  • サイズ:? (kmalloc-256)
  • base:tmr.functiontimerfd_tmrprocを指しているのでリーク可能。
  • heap:tmr.baseなどからリーク可能。
  • stack:リークできない。
  • RIP:取れそうで取れなさそう。
  • 確保:timerfd_createを呼ぶ。
  • 解放:tfdをcloseする?
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/fs/timerfd.c#L30
0x0000: 0xffff88800e216100
0x0008: 0x0000000000000000
0x0010: 0x0000000000000000
0x0018: 0x000000183ca77938
0x0020: 0x000000183ca77938
0x0028: 0xffffffff811e7ef0
0x0030: 0xffff88800f41ba80
...
[+] kbase = 0xffffffff81000000
[+] kbase = 0xffff88800f41ba80

tty_struct

  • サイズ:0x2e0 (kmalloc-1024)
  • base:opsptm_unix98_ops を指しているのでリーク可能。それ以外にも2箇所ほどkernelのdata領域を指していた。
  • heap:dev, driverなど多くのオブジェクトがヒープや自身のメンバを指しているのでリーク可能。対象のSLUBは未調査。
  • stack:リークできなさそう。
  • 確保:/dev/ptmxを開く。
  • 解放:開いたptmxを閉じる。
  • 備考:opsを書き換えることでRIPが制御可能。
  • 参考:https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/tty.h#L283
0x0000: 0x0000000100005401
0x0008: 0x0000000000000000
0x0010: 0xffff88800f1ed840
0x0018: 0xffffffff81e65900
0x0020: 0x0000000000000000
...
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800f1ed840
Press enter to continue...
[    5.413411] BUG: unable to handle kernel paging request at 00000000deadbeef

任意データ書き込み/Heap Sprayに使える構造体

msg_msg

説明済み

setxattr

  • サイズ:任意(<=65536)
  • 確保:setxattrを呼び出す。valueに書き込みたい値のポインタを入れ、sizeを指定する。
  • 解放:確保と同一パスで解放される。
  • 備考:userfaultfdと組み合わせて使う。msgsndでは先頭48バイトが書き換えられないので、それに対応したい場合に便利。ヒープスプレーにも使える。

sendmsg

  • サイズ:任意(>=2)
  • 確保:sendmsgを呼び出す。msg.msg_controlにポインタ、msg.msg_controllenにサイズを入れる。
  • 解放:確保と同一パスで解放される。
  • 備考:setxattrと同じでuserfaultfdと組み合わせる。

参考文献

[1] https://elixir.bootlin.com/linux/v4.19.98/source

[2] https://duasynt.com/blog/linux-kernel-heap-spray

[3] https://hama.hatenadiary.jp/entry/2018/12/01/000000

[4] https://smallkirby.hatenablog.com/entry/2019/11/19/225504