Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探

发布者:Gcow安全团队
发布于:2021-03-07 17:40

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,Pi
Name="Ethon"
Age=27
Hex=&h80000000
Pi=3.1415926

'隐式声明
Hello="ABC123"

赋值对应函数为vbscript!AssignVar,于该函数处设断,查看其参数:

图1

0x400C表示VT_VARIANT

图2

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

图3

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

图4

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

图5

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-safearraySAFEARRAYBOUND结构定义如下:

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_name

stu_name=Array("Alan","Susan","Lisa","Mary")

对应函数为vbscript!MakeArray

图6

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

图7

先来看一维数组的创建:

图8

rgsabound结构各字段赋值:

图9

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

图10

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

图11


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

图12

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

图13

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

图14


Redim语句用于重新定义数组大小:

'定义一维动态数组
Dim MyArray()
'重新定义该数组大小
ReDim MyArray(3)
MyArray(0) = "A"
MyArray(1) = "B"
MyArray(2) = "C"
MyArray(3) = "C"

再次调用vbscript!MakeArray过程如下:

图15

而在重新定义时加上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函数:

图16

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

图17

0x03 可用于调试时函数

IsEmpty(var)对应vbscript!VbsIsEmpty,其第三个参数对应var。一例:

<!doctype html>
<html>
<head>
</head>

<body>
<script LANGUAGE="VBScript">
   dim a
   a = &h1234
   IsEmpty(a)
</script>
</body>
</html>

图18

IsObject(var)对应vbscript!VbsIsObject,同样,其第三个参数对应var。一例:

<!doctype html>
<html>
<head>
</head>

<body>
<script LANGUAGE="VBScript">
   dim a
   a = &h1234
   IsObject(a)
</script>
</body>
</html>

图19

在调试时可借助这两个函数以确定变量值或内存位置。

0x04 VarType函数

<!doctype html>
<html>
<head>
</head>

<body>
<script LANGUAGE="VBScript">
   dim a
   a = &h1234
   VarType(a)
</script>
</body>
</html>

VarType对应vbscript!VbsVarType,其调用GetVarType函数获取类型值并完成赋值操作:

图20

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

图21

GetVarType函数调用PvarGetVarVal——判断类型值是否为0x4A0x0C

图22

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

图22

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

图23

0x05 LenB函数

<!doctype html>
<html>
<head>
</head>

<body>
<script LANGUAGE="VBScript">
On Error Resume Next
Dim length,a
a=1.12345678901235
length=LenB("Hello")
length=LenB(a)
</script>
</body>
</html>

该函数用于返回存储字符串字节大小,其对应vbscript!VbsLenB。参数为字符串时,该函数先是call VAR::BstrGetVal,返回pbstrVal

图24

之后call cbLengthBstr返回长度:

图25

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

图26

之后再进行计算:

图27

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

图28

0x06 参阅链接



声明:该文观点仅代表作者本人,转载请注明来自看雪