RPISEC/MBE: writeup lab6A (PIE)
TOC
源码来自https://github.com/RPISEC/MBE/blob/master/src/lab06/lab6A.c。这个程序相比 RPISEC/MBE:lab6B 要难一点。
分析
utils.h
/* |
lab6A.c
/* |
站长已经编译好了,点击下载lab6A
Writeup 借鉴自https://devel0pment.de/?p=378
漏洞
在函数setup_account里面,通过strncpy用最大字节数为32的结构体元素 user->name 来初始化结构体元素 user->desc。然后,字符串“is a”被追加到user->desc,这使得 user->desc 此时最长为 32 + 6 = 38 字节。然后追加变量temp的内容(最大为128字节)。总计38 + 128 = 166字节被复制到 user->desc。因为user->desc只有128字节长,所以会导致缓冲区溢出。
利用
我们现在可以控制指令指针(也就是 merchant.sfunc),但我们应该跳到哪里呢?我们既不知道任何绝对地址,也不知道共享库。我们的最终目标是生成一个shell。因为二进制文件中没有后门函数(win函数)并且我们不能在堆栈中存储和执行shell代码(启用了NX),所以我们应该尝试从libc调用system(“/bin/sh”)。在此之前,我们需要libc的地址。merchant.sfunc 包含函数print_listing的绝对地址,它至少是二进制文件的有效地址。我们可以进行部分覆盖来调用二进制的另一个函数。由于我们想泄漏libc地址,因此必须以某种方式打印一些东西,一个合适的候选可能是函数print_name:
void print_name(struct uinfo * info) { |
首先我们需要查找print_name 的偏移,下面的两个字节就是我们要覆盖的。
pwndbg> p print_name |
print_name位于偏移0xaff处。因为我们要覆盖的两个最不重要的字节仍然是4个随机比特,所以我编写了一个python脚本,它重复地将这两个字节设置为0xaff,若调用失败则重新调用,直到调用成功:
#!/usr/bin/python2 |
运行脚本结果如下:
[+] Starting local process './lab6A': Done |
输出中有两个地址:偏移0xaa的0x56630aff和偏移0xae的0xffb55190。
第一个地址就是sfunc的值。我们用0xaff覆盖了最不重要的两个字节。第二个地址也具有相同的基地址。不幸的是,这不是我们想要的,因为我们需要libc地址。
我们怎么能泄漏更多信息呢?还记得下面这行setup_account函数吗?
memcpy(user->desc + strlen(user->desc), temp, strlen(temp)); |
由于我们已经填满了user->desc,而strncpy函数和strcat函数都没有在user->desc中产生一个空字节,因此第二次调用 setup_account 函数时会覆盖上面的0xb3地方的\x00字节,覆盖之后则会打印出我们想要的地址信息。
用下面脚本表示:
#!/usr/bin/python2 |
结果如下:
[+] Starting local process './lab6A': Done |
我们从第二个地址后面泄漏了另一个地址:0xf7d6ee81,偏移为0xba。通过gdb调试可以确定这个地址是__libc_start_main 调用main 函数之后的ret 地址。现在我们只需要计算必要的偏移量,即可以泄露libc基地址。
pwndbg> x/i 0xf7d6ee81 |
下面就是该程序的exp脚本:
#!/usr/bin/python2 |