最近在刷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
在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
等打印函数,然而程序中并没有这些函数。只能通过syscall
和syscall_id
来调用需要的函数,但是又回到的最初没有syscall
的情况。
所以只能寻求一个更有创造性的方法。
在libc中搜索\x0f\x0f
–syscall
的机器码。可以看到在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
!
第一段payload完成溢出,并调用read
将第二段payload写入bss
段中,然后利用pop rbp;ret
和leave;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")
。
通过syscall
来调用函数必须要将syscall_id
放入rax
寄存器中,但是经过搜索并没有设置rax
相关的gadgets。必须利用函数的返回值来设置rax
。read
函数的返回值是读入字符的个数,所以可以通过调用read
函数来设置rax
的值。
通过写LSB得到syscall
后rax
的值为1,即可通过syscall
调用write
来泄漏内存,此时可以通过泄漏内存的字节数来控制rax
。sleep
的返回值是0,所以可以通过调用sleep
来将rax
置0调用read
。
所以既可以通过偏移计算system
的地址,也可以通过syscall
和syscall_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
因为主办方不让公开高分的题目,所以exploit提交到主办方的writeup系统中了,https://pwnable.tw/writeup/20/927