0x00 unexploitable

最近在刷pwnable.tw(类似pwnable.kr,不过是台湾的)的题,看到了一个unexploitable的题目。根据题目描述:

The original challenge is on pwnable.kr and it is solvable.
This time we fix the vulnerability and now we promise that the service is unexploitable.

下载下来后丢到ida看到确实没什么变化,除了pwnable.kr那个题中最重要的一点syscall没有给。

.text:0000000000400544 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400544 public main
.text:0000000000400544 main proc near ; DATA XREF: _start+1Do
.text:0000000000400544
.text:0000000000400544 buf = byte ptr -10h
.text:0000000000400544
.text:0000000000400544 push rbp
.text:0000000000400545 mov rbp, rsp
.text:0000000000400548 sub rsp, 10h
.text:000000000040054C mov edi, 3 ; seconds
.text:0000000000400551 mov eax, 0
.text:0000000000400556 call _sleep
.text:000000000040055B lea rax, [rbp+buf]
.text:000000000040055F mov edx, 100h ; nbytes
.text:0000000000400564 mov rsi, rax ; buf
.text:0000000000400567 mov edi, 0 ; fd
.text:000000000040056C mov eax, 0
.text:0000000000400571 call _read
.text:0000000000400576 leave
.text:0000000000400577 retn
.text:0000000000400577 main endp

0x01 思路

在pwnable.kr上这个题目用ROP和SROP均可以完成,不过有个重要个前提有syscall。这个题目删除了这个gadget,导致题目的难度上升了一个档次。pwnable.kr的writeup在http://weaponx.site/2017/02/28/unexploitable-Writeup-pwnable-kr/

题目给出了libc,看来是让我们用内存泄漏来计算出system/execve等地址来执行system("/bin/sh")。通常内存泄漏最终需要调用类似puts\write等打印函数,然而程序中并没有这些函数。只能通过syscallsyscall_id来调用需要的函数,但是又回到的最初没有syscall的情况。

所以只能寻求一个更有创造性的方法。

0x02 寻找syscall

在libc中搜索\x0f\x0fsyscall的机器码。可以看到在read函数中就有两个gadget。

gdb-peda$ find "\x0f\x05"
...
libc.so.6 : 0x7ffff7b0467e (<read+14>: syscall)
libc.so.6 : 0x7ffff7b0469b (<read+43>: syscall)
...
gdb-peda$ print read
$1 = {<text variable, no debug info>} 0x7ffff7b04670 <read>

可以发现read函数中的syscall的距离函数入口的偏移量只有14和43,根据ASLR的原理。可以通过修改GOT表中read函数的LSB(last significant bit,最低有效位)为0x7e或者0x9b就得到了syscall

0x03 how to exploit

第一段payload完成溢出,并调用read将第二段payload写入bss段中,然后利用pop rbp;retleave;ret将栈迁移到bss段上。

第二段payload完成修改GOT表中read的LSB,变为syscall。利用syscall调用write泄漏sleep的地址,计算出/bin/sh\x00的地址pop rdi;ret的地址和system的地址。最后将栈迁移到第三段payload上。

第三段payload,将/bin/sh\x00放入rdi寄存器中,完成system("/bin/sh\x00")

0x04 syscall to system

通过syscall来调用函数必须要将syscall_id放入rax寄存器中,但是经过搜索并没有设置rax相关的gadgets。必须利用函数的返回值来设置raxread函数的返回值是读入字符的个数,所以可以通过调用read函数来设置rax的值。

通过写LSB得到syscallrax的值为1,即可通过syscall调用write来泄漏内存,此时可以通过泄漏内存的字节数来控制raxsleep的返回值是0,所以可以通过调用sleep来将rax置0调用read

所以既可以通过偏移计算system的地址,也可以通过syscallsyscall_id = 59来调用execve

写出exploit后Boom shakalaka!

[+] [sleep base] => [0x7fb74adbe680]
[+] [system] => [0x7fb74ad14102]
[+] [pop rdi ret] => [0x7fb74ad38390]
FINAL!!!
[*] Switching to interactive mode
$ cat /home/unexploitable/flag
FLAG{********************************}
$
[*] Interrupted
[*] Closed connection to chall.pwnable.tw port 10403

0x05 exploit

因为主办方不让公开高分的题目,所以exploit提交到主办方的writeup系统中了,https://pwnable.tw/writeup/20/927