Kernel Exploitを初めて1ヶ月半くらいが経ちましたが、カーネルランドはデバッグが大変なので「なぜかexploitが動かない」ってなったときのチェックリストを作りました。 僕は大量に時間を溶かしたのですが、皆はこんな人間にならないでね。
権限昇格などをユーザーランドに書いて、それをROPから呼ぼうとしたら死ぬ場合があります。 これはSMEPが有効になっていることが原因です。 CR4を書き換えて事前にSMEPを無効化するか、ROPで済ましましょう。
また、SMEPを回避しているのにiretqでユーザーランドに戻ったら死ぬ場合があります。
これはKPTIが有効になっていることが原因です。
swapgs_restore_regs_and_return_to_usermode
のmov cr3, rdi;
以降を呼ぶなどして参照するページテーブルをユーザーランド用に戻しましょう。
また、ユーザーランドに戻るときはSMAPやKPTIに関係なくRSPをユーザーランドのアドレスに戻しましょう。
ROP chainに飛ぼうとしたり、ユーザーランドで用意したvtableを参照しようとしたりすると死ぬ場合があります。 これはSMAPが有効になっていることが原因です。 CR4やeflagsを書き換えて事前にSMAPを無効化するか、使いたいデータをカーネルランドに用意しましょう。
ret2usrでユーザーランドで用意したコードを実行中に死ぬ場合があります。 これはカーネルランドから呼べない関数を呼ぼうとしている可能性があります。 例えば
void privilege_escalation() { puts("[+] win!"); cc(pkc(0)); }
はputsをカーネルランドから呼ぼうとするので死にます。
rspが壊れている場合があります。 iretqする際に設定したrspが読み書き可能かを確認しましょう。 元のrspじゃなくても良いので、mmapした領域などをrspにしれやれば良いです。 この時mmapで0x10000バイトほど確保し、オフセット0x8000くらいを新しいrspに設定すればexecveなどは正しく動きます。
スタック領域が十分に足りていない可能性があります。 私はiretqで戻すRSPは、読み書き可能な領域として0x8000バイト余裕を持たせています。
よくRIPが取れているかを確認するために0xdeadbeefや0xcafebabeに飛ばします。 ここで落ちない場合、そのアドレスがすでに確保されている可能性があります。 0xfee1deadや0xffffffffffffffffなども試して確認しましょう。
ユーザーランドに戻った後に
mov hoge, fs:[huga]
みたいな箇所で急にジャンプしたり死んだりする場合があります。
私が詰まった時はsocket経由でripを取ってユーザーランドに戻った際にこの現象に陥りました。
syscall
も同様に使えません。
fsが壊れていることが原因っぽいのですが、この現象の解決策は分かりません。 ただ他にもこれに遭遇している方がいたのでチェックリストに記載しました。 別の方法でRIPを取ることが現状の解決策です。
ユーザーランドに戻ってsyscallを実行すると、実行直後にTrace/breakpoint trap
になる場合があります。
これはeflagsが書き換わっていることが原因です。使ったgadgetの途中にpopfq
でも挟まっていたのでしょう。
popfq
でeflagsを元の値に戻せば直ります。(元の値はgdbで確認)
gdbでデバッグしている場合、gdbでアタッチしないと動く場合があります。 理由はよく分かりません。
どうしても /bin/sh
が起動できない場合があるかもしれません。
ユーザーランド側で落ちてシェルが取れないけどカーネルは落ちない、という場合はカーネルランドでファイルの権限を変更するのも手です。
run_cmd
などを使って/bin/chmod 777 /flag
等を動かせばフラグが手に入ることもあります。
ただ、これは大抵exploitが悪いのでちゃんと直せばrootでシェルが降ってくるはずです。
なお、ユーザーランドに戻れない場合はカーネルランドでmsleepで待機させて、バックグラウンドで実行している間にやりたいことをやります。