Linuxのシステムコールのファジングツールとしてsyzkallerというのがあって、これはコードカバレッジを見つつ入力を変えていってくれるというファジングするツールです。 試してみたのでどんな感じなのかを簡単にめも。
まず、ツール自体はgolangで書かれているのでgoの実行環境が必要です。あとテスト対象のカーネル側でコードカバレッジを利用可能になっている必要があります。この辺はsyzkallerのドキュメントに書かれています。 ツールはビルドして行います。make一発で良いのですが、デフォルトだとstatic linkなバイナリを作ります。うちだと-lpthreadと-lcのところでそんなライブラリはないとか怒られてしまったので、動的リンクにしました。ホストとゲストのディストリビューションが同じならライブラリのABIの違いとかないですし、これで良いでしょう。
ファジングはVMを使って行います。自分はqemuでやりました。qemuを使う場合、Linuxがインストールされた環境が必要です。自分はホストとゲストはともにfedora 25でやりました。ゲストのイメージを作るのは面倒だったので既存のVMを/var/lib/libvirt/imagesからコピーしてきて使った感じです。
syzkallerはwebのコンソールもあって現在の状況とかクラッシュしたときの情報、コードカバレッジを見ることができます。簡単な使い方はsyz-managerというツールに設定ファイルを引数として渡して実行するパターンです。今回はこれでやりました。この場合、ファジングを行うバイナリはscpで転送して、ゲスト側でそのバイナリで実行するようです。
では、実際に試してみます。と言っても実際にクラッシュしたところを見たいので特定の条件でクラッシュするようにパッチを当てます。 unshare(2)でCLONE_NEWUTSとCLONE_NEWPIDがセットされている場合にpanic()を呼ぶようにしたカーネルで実験します。
masami@saga:~/codes/linux-stable (4.11.5 *)$ git diff diff --git a/kernel/fork.c b/kernel/fork.c index 4f7151d..4e3fe8a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2273,6 +2273,8 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) int do_sysvsem = 0; int err; + if (unshare_flags & CLONE_NEWUTS && unshare_flags & CLONE_NEWPID) + panic("syscall fuzzer found it!"); /* * If unsharing a user namespace must also unshare the thread group * and unshare the filesystem root and working directories. masami@saga:~/codes/linux-stable (4.11.5 *)$
次に設定ファイルです。これはこのようにしました。だいたい見たとおりの設定内容ですね。vmのところはqemuで使う設定です。自分が使っているイメージはパーティションが2つあるのでcmdlineでrootファイルシステムを/dev/sda2に設定しています。unshare(2)以外をテストする必要はないのでenable_syscallsでunshare(2)だけを指定しています。
masami@saga:~/go/src/github.com/google/syzkaller (master %=)$ cat ktest-config.cfg { "http": "localhost:56741", "workdir": "/home/masami/tmp/syzkaller_tmp", "vmlinux": "/home/masami/tmp/vmlinux", "image": "/home/masami/ktest/linux-ktest.img", "syzkaller": "/home/masami/go/src/github.com/google/syzkaller", "enable_syscalls": [ "unshare" ], "procs": 4, "type": "qemu", "leak": false, "vm": { "count": 4, "cpu": 2, "mem": 2048, "kernel": "/home/masami/tmp/bzImage", "initrd": "/home/masami/tmp/initramfs-ktest.img", "sshkey": "/home/masami/.ssh/id_rsa_nopass", "cmdline": "root=/dev/sda2" } }
これで以下のようにコマンドを実行するとファジング開始します。
masami@saga:~/go/src/github.com/google/syzkaller (master %=)$ ./bin/syz-manager -config ./ktest-config.cfg
実行開始してwebコンソールを開くとこんなふうな画面が見れます。
Crashesのところにcrashした情報が表示されます。下のほうにはコードカバレッジのリンクがあります。
コードカバレッジはこんな画面で赤いところがクラッシュした行、実行した行には /* covered */
というコメントが付きます。
OOPSが出たときの情報はこのように見えます。これはunshare(2)に渡した引数とその結果っぽいですね。
Crashesのリンクをクリックするとhas C reproというリンクがあります。
これをクリックすると以下のようにコードが見れます。これはバグの再現プログラムです。
このプログラムをホストがゲストでコンパイルして実行するとoopsします。qemuを起動させていおいて、
masami@saga:~/tmp$ qemu-system-x86_64 -enable-kvm -kernel ./bzImage -initrd ./initramfs-ktest.img -hda ~/ktest/linux-ktest.img -append root=/dev/sda2 -m 2048 -net nic -net user,host=10.0.2.10,hostfwd=tcp::45243-:22
別のターミナルでコードをコンパイルしてscpで転送して、sshでログインして実行。a.outの実行後に文字が出てないのはoopsが出てるからです。
masami@saga:~$ vi test.c masami@saga:~$ gcc test.c masami@saga:~$ scp -i ~/.ssh/id_rsa_nopass -P 45243 a.out root@localhost:~/. a.out 100% 8536 9.2MB/s 00:00 masami@saga:~$ ssh -i ~/.ssh/id_rsa_nopass -p 45243 root@localhost Last login: Sun Jun 18 19:19:06 2017 from 10.0.2.10 [root@linux-ktest ~]# ./a.out
oopsでてます。
syzkallerはコードカバレッジを見て入力を変えていくのと、再現プログラムも残してくれるので良いですね。

サイバーセキュリティテスト完全ガイド ~Kali Linuxによるペネトレーションテスト~
- 作者: Peter Kim,八木橋優,前田優人,美濃圭佑,保要隆明,株式会社クイープ
- 出版社/メーカー: マイナビ出版
- 発売日: 2016/08/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る