Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探
Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探
1.本文一共1514个字 28张图 预计阅读时间10分钟2.本文作者erfze 属于Gcow安全团队复眼小组 未经过许可禁止转载3.本篇文章是文章Internet Explorer漏洞分析(三)[下]——CVE-2014-6332的前置知识,对vbscrip.dll组件进行逆向分析,以及VBScript数据类型,数组,VarType函数,LenB函数详细分析,并介绍VBS脚本调试技巧4.本篇文章十分适合漏洞安全研究人员进行交流学习5.若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽
近来分析Internet Explorer历史漏洞,遂对VBScript脚本解析引擎进行研究,具体环境如下:
•OS版本:Windows 7 Service Pack 1•Internet Explorer版本:8.0.7601.17514•vbscript.dll版本:5.8.7601.17514
0x01 变量
VBScript中仅有一种数据类型——Variant。其结构定义如下:
typedef struct tagVARIANT {union {struct {VARTYPE vt;WORD wReserved1;WORD wReserved2;WORD wReserved3;union {LONGLONG llVal;LONG lVal;BYTE bVal;SHORT iVal;FLOAT fltVal;DOUBLE dblVal;VARIANT_BOOL boolVal;VARIANT_BOOL __OBSOLETE__VARIANT_BOOL;SCODE scode;CY cyVal;DATE date;BSTR bstrVal;IUnknown *punkVal;IDispatch *pdispVal;SAFEARRAY *parray;BYTE *pbVal;SHORT *piVal;LONG *plVal;LONGLONG *pllVal;FLOAT *pfltVal;DOUBLE *pdblVal;VARIANT_BOOL *pboolVal;VARIANT_BOOL *__OBSOLETE__VARIANT_PBOOL;SCODE *pscode;CY *pcyVal;DATE *pdate;BSTR *pbstrVal;IUnknown **ppunkVal;IDispatch **ppdispVal;SAFEARRAY **pparray;VARIANT *pvarVal;PVOID byref;CHAR cVal;USHORT uiVal;ULONG ulVal;ULONGLONG ullVal;INT intVal;UINT uintVal;DECIMAL *pdecVal;CHAR *pcVal;USHORT *puiVal;ULONG *pulVal;ULONGLONG *pullVal;INT *pintVal;UINT *puintVal;struct {PVOID pvRecord;IRecordInfo *pRecInfo;} __VARIANT_NAME_4;} __VARIANT_NAME_3;} __VARIANT_NAME_2;DECIMAL decVal;} __VARIANT_NAME_1;} VARIANT;
其中VARTYPE可参阅[Microsoft Docs——VARIANT Type Constants]https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f。例:
'显式声明Dim Name,Age,Hex,PiName="Ethon"Age=27Hex=&h80000000Pi=3.1415926'隐式声明Hello="ABC123"
赋值对应函数为vbscript!AssignVar,于该函数处设断,查看其参数:

0x400C表示VT_VARIANT:

判断pvargSrc—>vt值(具体数值可自行分析,不赘述),若均不满足,执行如下语句:

简单来说,即VariantCopyInd(&pvarDest, pvargSrc)——>copy pvarDest to pvarg:

隐式声明变量其pvarg全为零:

0x02 数组
数组存储结构由SAFEARRAY定义:
typedef struct tagSAFEARRAY {USHORT cDims;USHORT fFeatures;ULONG cbElements;ULONG cLocks;PVOID pvData;SAFEARRAYBOUND rgsabound[1];} SAFEARRAY;
其中各字段含义可参阅[Microsoft Docs——SAFEARRAY]https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray ,SAFEARRAYBOUND结构定义如下:
typedef struct tagSAFEARRAYBOUND {ULONG cElements;LONG lLbound;} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
数组定义及赋值操作:
Dim stu_name(3)stu_name(0)="Alan"stu_name(1)="Susan"stu_name(2)="Lisa"stu_name(3)="Mary"
VBS中数组下标由0开始,数组元素个数为n+1(Dim array_name(n))。另一种定义数组方法:
Dim stu_namestu_name=Array("Alan","Susan","Lisa","Mary")
对应函数为vbscript!MakeArray:

传递给函数的参数有二——cDims对应维数,VAR对应n。cDims应介于1-64:

先来看一维数组的创建:

为rgsabound结构各字段赋值:

之后则直接调用SafeArrayCreate函数进行创建,各参数含义可参阅[Microsoft Docs——SafeArrayCreate]https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreate 。创建完成:

为数组元素赋值则直接将该元素所在内存偏移传递给vbscript!AssignVar:

下面来看看二维数组(Dim stu_name(2,3))创建过程:

可以看到数组各维大小于内存中并列存储,之后调用VAR::PvarGetTypeVal逐一读取为rgsabound中cElements字段赋值:

各维大小于内存中由最高维——>最低维存储,故读取时首先计算出v3变量指向最低维大小所在内存偏移,之后递减。创建完成:

Redim语句用于重新定义数组大小:
'定义一维动态数组Dim MyArray()'重新定义该数组大小ReDim MyArray(3)MyArray(0) = "A"MyArray(1) = "B"MyArray(2) = "C"MyArray(3) = "C"
再次调用vbscript!MakeArray过程如下:

而在重新定义时加上Preserve关键字用于保留之前元素:
Dim MyArray()ReDim MyArray(3)MyArray(0) = "A"MyArray(1) = "B"MyArray(2) = "C"MyArray(3) = "D"ReDim Preserve MyArray(5)MyArray(4) = "E"MyArray(5) = "F"
其对应vbscript!RedimPreserveArray函数:

为psaboundNew各字段赋值完成后传递给SafeArrayRedim函数:

0x03 可用于调试时函数
IsEmpty(var)对应vbscript!VbsIsEmpty,其第三个参数对应var。一例:
<!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript">dim aa = &h1234IsEmpty(a)</script></body></html>

IsObject(var)对应vbscript!VbsIsObject,同样,其第三个参数对应var。一例:
<!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript">dim aa = &h1234IsObject(a)</script></body></html>

在调试时可借助这两个函数以确定变量值或内存位置。
0x04 VarType函数
<!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript">dim aa = &h1234VarType(a)</script></body></html>
VarType对应vbscript!VbsVarType,其调用GetVarType函数获取类型值并完成赋值操作:

参数1用于存储类型值,参数2为VarType参数:

GetVarType函数调用PvarGetVarVal——判断类型值是否为0x4A或0x0C:

之后与0x09进行比较,若不是则直接返回对象进而获取vt值:

由VbsVarType函数完成最终赋值给参数1操作:

0x05 LenB函数
<!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript">On Error Resume NextDim length,aa=1.12345678901235length=LenB("Hello")length=LenB(a)</script></body></html>
该函数用于返回存储字符串字节大小,其对应vbscript!VbsLenB。参数为字符串时,该函数先是call VAR::BstrGetVal,返回pbstrVal:

之后call cbLengthBstr返回长度:

参数为变量时, VAR::BstrGetVal函数调用VAR::PvarConvert,将其内容转换为字符串:

之后再进行计算:

cbLengthBstr函数功能仅是读取字符串存储位置之前4字节内容,该内容为字符串长度:

0x06 参阅链接
•[Microsoft Docs——SAFEARRAY structure]https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray•[Microsoft Docs——VARIANT structure]https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant•[Microsoft Docs——VARIANT Type Constants]https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f•[Microsoft Docs——SafeArrayCreate]https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreate•[Microsoft Docs——DECIMAL structure]https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ns-wtypes-decimal-r1
