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

Gcow安全團隊發表於2021-03-07

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 參閱連結


相關文章