カーネルの起動方法、デバッグ方法、そしてセキュリティなど、Kernel Exploitを始めるのに必要な知識は完璧に習得しました。ここからは、実際にexploitをどのように書いていくのかと、書いたexploitをどうやってqemu上で動かすかを学びます。
qemu上での実行
qemuの上でexploitを書いてビルド、実行すると、カーネルがクラッシュする度にやり直す必要があるので大変です。そのため、C言語で書いたexploitを手元でビルドしてから、それをqemuに送る必要があります。
この流れを毎回コマンド入力するのは大変なので、シェルスクリプトなどでテンプレートとして用意しておきましょう。例えば次のようなtransfer.sh
を用意してみます。
1 |
|
説明するまでもありませんが、単にGCCでexploit.c
をコンパイルしてcpioに追加し、qemuを起動しているだけです。元のrootfs.cpio
を壊さないようにdebugfs.cpio
という名前のディスクを使っていますが、お好みで変更しても構いません。
また、cpioを作る際はroot権限でないとファイルの権限が変わるので、transfer.sh
はroot権限で実行するよう注意してください。
さて、exploit.c
に次のようなコードを入れてtransfer.sh
を実行してみましょう。
1 |
|
すると、次のようにエラーが出てしまいます。なぜでしょうか。
実は今回配布したイメージは通常のlibcではなく、uClibcというコンパクトなライブラリを使っています。当然exploitをコンパイルしたみなさんの環境ではGCC、すなわちlibcを使っているので、動的リンクに失敗してexploitは動きません。
したがって、qemu上でexploitを動かす際はstaticリンクするように注意しましょう。
1 | gcc exploit.c -o exploit -static |
このように変更して実行すれば、プログラムが動くはずです。
リモートマシンでの実行:musl-gccの利用
ここまでで、無事exploitをqemu上で実行できました。今回配布した環境はネットワーク接続できるように設定してあるため、リモートで実行したい場合はqemu上でwgetコマンドなどを利用してexploitを転送できます。
しかし、CTFなど一部の小さい環境ではネットワークが利用できません。このような場合、busyboxに存在するコマンドを利用してリモートからバイナリを転送する必要があります。一般的にはbase64が使われるのですが、GCCでビルドしたファイルは数百KBから数十MBにもなるため、転送に非常に時間がかかります。サイズが大きくなるのは外部ライブラリ(libc)の関数をstaticリンクしているのが原因です。
GCCでサイズを小さくしたければ、libcを使わないようにし、readやwriteなどはシステムコール(インラインアセンブリ)を使って自分で定義する必要があります。もちろんこれは非常に大変です。
そこで、多くのCTFerはKernel Exploitの目的でmusl-gccと呼ばれるCのコンパイラを利用しています。以下のリンクからダウンロードし、ビルドしてインストールを完了させてください。
インストールが完了したら、次のようにtransfer.sh
のコンパイル箇所を書き換えてみましょう。musl-gccのパスは各自インストールした先のディレクトリを指定してください。
1 | /usr/local/musl/bin/musl-gcc exploit.c -o exploit -static |
著者の環境では先ほどのHello, Worldプログラムは、gccの場合851KB、musl-gccの場合18KBでした。さらに小さくしたい場合はstripなどでデバッグシンボルを削除しても構いません。
一部のヘッダファイル(Linuxカーネル系)はmusl-gccにはないから、インクルードパスを設定するかgccでコンパイルする必要があるよ。そういうときは一度アセンブリを経由してビルドすれば、gccの機能を使いつつファイルサイズを抑えられるね。
$ gcc -S sample.c -o sample.S
$ musl-gcc sample.S -o sample.elf
ここまで完了したら、リモートに(nc経由で)base64を使ってバイナリを転送するスクリプトを書きましょう。このアップローダはCTFの場合は毎回使いますので、テンプレートとして自分用のものを作っておくことをおすすめします。
1 | from ptrlib import * |
実行してしばらくすると次のようにアップロードが完了するはずです。
このサイトではみなさんが手元で試すだけなのでアップロードは不要ですが、CTFなどで実践する際はこれを思い出して使いましょう。