參考:
????????使用Syzkaller&QEMU捕捉內(nèi)核堆溢出Demo? ?
????????內(nèi)核漏洞挖掘技術(shù)系列(4)——syzkaller(1)?
????????實驗所需源碼
本文主要參考第一篇文章,但是文章中很多步驟不全面辱魁,而且存在錯誤烟瞧,故記錄本實驗過程。本實驗前提是參照配置syzkaller&QEMU環(huán)境配置好syzkaller環(huán)境染簇。
本文將演示使用Syzkaller聯(lián)合QEMU觸發(fā)一個內(nèi)核溢出的bug参滴。 包括三個步驟:
????????(1)在syzkaller中添加規(guī)則觸發(fā)堆溢出?
????????(2)編譯一個帶有堆溢出模塊的kernel
????????(3)運(yùn)行syzkaller&QEMU捕捉內(nèi)核堆溢出
1.在syzkaller中添加規(guī)則
本例新增調(diào)用模板見proc_operation.txt,/syzkaller/sys/linux目錄下的sys.txt中有通用的調(diào)用形式可以參考锻弓。
解釋:syzkaller使用它自己的聲明式語言來描述系統(tǒng)調(diào)用模板砾赔,docs目錄下的syscall_descriptions.md中可以找到相關(guān)的說明。這些系統(tǒng)調(diào)用模板被翻譯成syzkaller使用的代碼需要經(jīng)過兩個步驟弥咪。第一步是使用syz-extract從linux源代碼中提取符號常量的值过蹂,結(jié)果被存儲在.const文件中十绑,例如/sys/linux/tty.txt被轉(zhuǎn)換為sys/linux/tty_amd64.const聚至。第二步是根據(jù)系統(tǒng)調(diào)用模板和第一步中生成的const文件使用syz-sysgen生成syzkaller用的go代碼”境龋可以在/sys/linux/gen/amd64.go和/executor/syscalls.h中看到結(jié)果扳躬。最后,重新編譯生成帶有相應(yīng)規(guī)則的syzkaller二進(jìn)制可執(zhí)行文件。
調(diào)用規(guī)則:$號前的syscallname是系統(tǒng)調(diào)用名贷币,$號后的type是指特定類型的系統(tǒng)調(diào)用击胜。如上文的 open$proc 指的就是open這個類調(diào)用中proc這個具體的調(diào)用,這個名字是由規(guī)則編寫者確定的役纹,具體行為靠的是后面的參數(shù)去確定偶摔。 參數(shù)的格式如下: ArgumentName ArgumentType[Limit] ArgumentName是指參數(shù)名,ArgumentType指的是參數(shù)類型促脉,例如上述例子有string辰斋、flags等類型。[ ]號中的內(nèi)容就是具體的類型的值瘸味,不指定的時候由syzkaller自動生成宫仗,若要指定須在后文指定,以上文為例:
????????????mode flags[proc_open_mode]
????????????proc_open_mode = ...
????????因為我們給的例子是通過/proc/test這個內(nèi)核接口的寫操作來觸發(fā)堆溢出旁仿,因此我們需要控制的參數(shù)是open函數(shù)中的file參數(shù)為“/proc/test”即可藕夫,其他操作參考sys.txt即可。
步驟:
? ? (1)編譯生成syz-extract
? ? ? ? ?~/Desktop/ctf/kernel_fuzz/gopath/src/github.com/google/syzkaller$ make bin/syz-extract
? ? (2)編譯生成syz-sysgen
????????~/Desktop/ctf/kernel_fuzz/gopath/src/github.com/google/syzkaller$ make bin/syz-sysgen
? ? (3)用syz-extract生成.const文件(本例proc_operation.txt有錯誤枯冈,read和write不需要返回值)可選擇只生成你新增的.txt
????????~/Desktop/ctf/kernel_fuzz/gopath/src/github.com/google/syzkaller$ bin/syz-extract -os linux -sourcedir "/home/john/Desktop/ctf/kernel_
fuzz/linux/linux" -arch amd64 sys/linux/proc_operation.txt?
????????同目錄下生成了proc_operation_amd64.const毅贮。
? ? (4)然后運(yùn)行syz-sysgen
????????~/Desktop/ctf/kernel_fuzz/gopath/src/github.com/google/syzkaller$ bin/syz-sysgen
? ? (5)重編譯syzkaller
????????進(jìn)入syzkaller源碼目錄,運(yùn)行:
????????????$ make clean
????????????$ make all
? ? (6)拷貝編譯好的syz-fuzzer到目標(biāo)系統(tǒng):
????????????scp -P 10021 -i ./stretch.id_rsa -r ../gopath/bin root@127.0.0.1:/root/bin
2.編譯帶有堆溢出的內(nèi)核模塊
漏洞:代碼見test.c霜幼,主要是proc_write()函數(shù)有堆溢出嫩码。
編譯:參考https://www.cnblogs.com/zhangjy6/p/5462644.html
????????????$ make
????????????$ scp -P 10021 -i ./stretch.id_rsa -r ./test/test.ko root@127.0.0.1:/root/bin
????????????目標(biāo)機(jī)器上$ sudo insmod test.ko
問題:無法insmod
????????我本機(jī)linux-headers版本是4.4.0-103-generic,目標(biāo)內(nèi)核版本是5.2.0-rc1+(查看版本命令$ uname -r)罪既,編譯出來的driver在目標(biāo)機(jī)上無法ismod铸题,需安裝linux-headers-5.2.0-rc1+。
解決:安裝linux-headers-5.2.0-rc1+方法琢感,先在網(wǎng)頁上找到對應(yīng)版本的headers丢间。
????????$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.2-rc1/linux-headers-5.2.0-050200rc1_5.2.0-050200rc1.201905191930_all.deb
????????$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.2-rc1/linux-headers-5.2.0-050200rc1-generic_5.2.0-050200rc1.201905191930_amd64.deb
????????$ sudo dpkg -i *.deb
????????修改KDIR =/usr/src/linux-headers-5.2.0-050200rc1-generic
結(jié)果:還是不匹配,很無奈驹针,要么重新編譯內(nèi)核吧烘挫,我選擇linux-4.4.0.184版本,大不了把本機(jī)內(nèi)核也更新為這個版本柬甥。(參考https://www.itsmearunchandel.co.in/linux/ubuntu/upgrade-kernel-version-in-ubuntu.html; 內(nèi)核源碼https://kernel.ubuntu.com/~kernel-ppa/mainline/v4.4.184/)
問題:應(yīng)該更新了也還是不行饮六,linux-headers-5.2.0-050200rc1-generic跟linux-headers-5.2.0-rc1+還是不匹配。
解決:將驅(qū)動模塊編譯進(jìn)內(nèi)核苛蒲。所以要么把模塊編譯到目標(biāo)內(nèi)核卤橄,要么魔改許多module里的數(shù)據(jù)結(jié)構(gòu)進(jìn)行錯版本加載。
????????(參考https://blog.csdn.net/qq_33487044/article/details/81949703和https://kouriba.iteye.com/blog/1632767)
???????? make menuconfig->Enable loadable module support->Forced module loading(同時關(guān)閉Module versioning support臂外,只開啟前3個)窟扑。
????????修改drvier目錄下的MAKEFILE文件喇颁,在最后一行添加: obj-y += testmod/
????????然后在testmode目錄下,添加一個MAKEFILE 文件嚎货,文件的內(nèi)容為:obj-m := hello.o
步驟如下:
????????(1)test.c拷貝到/linux/drivers/char/目錄下
????????(2)/linux/drivers/char/Kconfig下添加
? ? ????????menu "Character devices"? ? ? ? ? ? #已有
? ? ????????config TEST_MODULE
????????????tristate "Heap Overflow Test"? ? ????#在menuconfig中顯示的名字
????????????default y? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #默認(rèn)選項
????????????help
????????????????This file is to test a buffer overflow.
? ? ????????endmenu? ? ? ? ? ? ? #已有
????????(3)/linux/drivers/char/Makefile下添加
? ????????????? obj-$(CONFIG_TEST_MODULE) += test.o
????????(4)如果/linux/drivers/char/是新目錄橘霎,需修改/linux/drivers/Kconfig(加上source "drivers/char/Kconfig");修改/linux/drivers/Makefile(加上obj-$(CONFIG_TEST_MODULE) += char/)殖属。
????????(5)配置文件選擇該模塊$ make menuconfig -> Device Drivers -> Character devices -> Heap Overflow Test? (*表示直接編如內(nèi)核姐叁,M表示模塊形式,)
????????(6)運(yùn)行內(nèi)核洗显,查看模塊是否加載
????????????????$ls /proc/test? 或? ? $dmesg|grep test
3.運(yùn)行syzkaller捕捉溢出
(1)修改my.cfg(只允許某些調(diào)用七蜘,速度更快):
????"enable_syscalls": [
? ? ? ? ? ? ? ? "open$proc",
? ? ? ? ? ? ? ? "read$proc",
? ? ? ? ? ? ? ? "write$proc",
? ? ? ? ? ? ? ? "close$proc"
????????],
(2)運(yùn)行虛機(jī)測試:?
? ? ? ? $ bin/syz-manager -config ./my.cfg? -v 10
????????用瀏覽器打開“127.0.0.1:56741”,運(yùn)行一段時間后出現(xiàn)以下日志:
```
PROC_DEV:into open!
==================================================================
BUG: KASAN: slab-out-of-bounds in copy_from_user arch/x86/include/asm/uaccess.h:698 [inline] at addr ffff88003c1f5e20
BUG: KASAN: slab-out-of-bounds in proc_write+0x64/0x90 drivers/mod_test/test.c:45 at addr ffff88003c1f5e20
Write of size 4096 by task syz-executor0/2569
CPU: 0 PID: 2569 Comm: syz-executor0 Not tainted 4.11.0-rc8+ #23
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014
Call Trace:
__dump_stack lib/dump_stack.c:16 [inline]
dump_stack+0x95/0xe8 lib/dump_stack.c:52
kasan_object_err+0x1c/0x70 mm/kasan/report.c:164
print_address_description mm/kasan/report.c:202 [inline]
kasan_report_error mm/kasan/report.c:291 [inline]
kasan_report+0x252/0x510 mm/kasan/report.c:347
check_memory_region_inline mm/kasan/kasan.c:326 [inline]
check_memory_region+0x13c/0x1a0 mm/kasan/kasan.c:333
kasan_check_write+0x14/0x20 mm/kasan/kasan.c:344
copy_from_user arch/x86/include/asm/uaccess.h:698 [inline]
proc_write+0x64/0x90 drivers/mod_test/test.c:45
proc_reg_write+0xf6/0x180 fs/proc/inode.c:230
__vfs_write+0x10b/0x560 fs/read_write.c:508
vfs_write+0x187/0x520 fs/read_write.c:558
SYSC_write fs/read_write.c:605 [inline]
SyS_write+0xd4/0x1a0 fs/read_write.c:597
entry_SYSCALL_64_fastpath+0x18/0xad
RIP: 0033:0x450a09
RSP: 002b:00007ff6efd15b68 EFLAGS: 00000216 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00000000006f8000 RCX: 0000000000450a09
RDX: 0000000000000090 RSI: 0000000020d09000 RDI: 0000000000000005
RBP: 0000000000000046 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000216 R12: 0000000000000000
R13: 00007ffc210fd8ff R14: 00007ff6efd16700 R15: 0000000000000000
Object at ffff88003c1f5e20, in cache kmalloc-512 size: 512
...
Dumping ftrace buffer:
? (ftrace buffer empty)
Kernel Offset: disabled
```
????????這就是內(nèi)核被crash時打印出來的調(diào)用信息墙懂,我們可以看到溢出發(fā)生在proc_write+0x64/0x90 drivers/mod_test/test.c:45處橡卤,也就是我們添加進(jìn)去的帶有堆溢出的模塊。說明我們用Syzkaller添加規(guī)則捕捉到了堆溢出的代碼损搬。
? ? ? ? 結(jié)果我啥都沒跑出來碧库。
4.漏洞復(fù)現(xiàn)
????????workdir/crashes目錄下包含crash之前執(zhí)行的程序。/tools目錄下幾個工具值得關(guān)注:syz-execprog以各種模式執(zhí)行單個或一組程序巧勤,首先在循環(huán)中運(yùn)行l(wèi)og中所有的程序來確認(rèn)確實它們之一引發(fā)了crash嵌灰。
????????$./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 crash-log
????????然后嘗試識別是哪個程序?qū)е碌腸rash。
????????$./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 single-program
????????syz-execprog在本地執(zhí)行程序颅悉,所以需要將syz-execprog和syz-executor復(fù)制到帶有測試內(nèi)核的VM中并在那里運(yùn)行它沽瞭。一旦確認(rèn)了引發(fā)崩潰的單個程序,嘗試通過注釋掉單個系統(tǒng)調(diào)用并刪除無關(guān)的數(shù)據(jù)來縮小范圍剩瓶。還可以嘗試將所有mmap調(diào)用合并為一個驹溃。給syz-execprog加上-threaded=0 -collide=0標(biāo)志確認(rèn)這個程序仍然能夠?qū)е耤rash。
????????如果不能復(fù)現(xiàn)的話延曙,嘗試把每個系統(tǒng)調(diào)用移到單獨(dú)的線程中[4]豌鹤。然后通過syz-prog2c得到C程序,C程序應(yīng)該也可以導(dǎo)致crash枝缔。這個過程在某種程度上也可以通過syz-repro自動化布疙,需要提供config文件和crash報告。
$./syz-repro -config my.cfg crash-qemu-1-1455745459265726910