Linuxのsystem call fuzzer「syzkaller」めも

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コンソールを開くとこんなふうな画面が見れます。

f:id:masami256:20170618190630p:plain 

Crashesのところにcrashした情報が表示されます。下のほうにはコードカバレッジのリンクがあります。

コードカバレッジはこんな画面で赤いところがクラッシュした行、実行した行には /* covered */ というコメントが付きます。

f:id:masami256:20170618191013p:plain

OOPSが出たときの情報はこのように見えます。これはunshare(2)に渡した引数とその結果っぽいですね。

f:id:masami256:20170618191241p:plain

Crashesのリンクをクリックするとhas C reproというリンクがあります。

f:id:masami256:20170618191448p:plain

これをクリックすると以下のようにコードが見れます。これはバグの再現プログラムです。

f:id:masami256:20170618191339p:plain

このプログラムをホストがゲストでコンパイルして実行すると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でてます。

f:id:masami256:20170618192320p:plain

syzkallerはコードカバレッジを見て入力を変えていくのと、再現プログラムも残してくれるので良いですね。

サイバーセキュリティテスト完全ガイド ~Kali Linuxによるペネトレーションテスト~

サイバーセキュリティテスト完全ガイド ~Kali Linuxによるペネトレーションテスト~