有关MS14-066 / CVE-2014-6321,即winshock漏洞已经被大家沸沸扬扬关注很久了。由于影响甚广,至今没有poc公布。Beyondtrust公司率先放出截图触发了该漏洞,随后也有安全大牛纷纷实现漏洞崩溃。近日安全研究员Mike Czumak放出了一份更详细的报告,依然没有给出poc。下面随笔者来看下,这个漏洞究竟是怎么回事。
进入正确的分支
这里是触发漏洞的关键代码逻辑处,位于schannel.dll的DecodeSigAndReverse起始处:
进入DecodeSigAndReverse函数后首先调用CryptDecodeObject函数,然后判断返回值走入不同的分支,其中左下角黄色代码块中的两次memcpy是引起漏洞的关键所在,所以必须让程序流程判断cmp ebx,2fh正确跳转。即判断是否为ECC。
此漏洞影响带有SSL的IIS服务器,以及远程桌面。
本次调试采用win7+IIS环境,在443端口开启https服务。
首先,用OpenSSL创建自签名证书,并且导入IIS中应用。(此处过程略,大家都会)
需要注意的是要双机调试获得内核调试权限,进入lsass.exe进程上下文,再下断点。因为windows的SSL验证都是经过这个进程的。
然后下载1.0.1j版本的openssl源码并修改,尝试触发漏洞。
修改的地方有两处:
S3_clnt.c的437行处。
重新编译openssl之后用来访问刚才架设好的IIS主机,触发漏洞点的函数DecodeSigAndReverse:
促使程序流程走到DecodeSigAndReverse,相信大部分安全研究者都可以达到这一步。
下面继续来看,满足什么样的条件可以走到memcpy的分支。
细节分析
请注意看DecodeSigAndReverse一开始调用了两次CryptDecodeObject,对照MSDN的解释,从代码逻辑中我们可知,这第一次的CryptDecodeObject调用将pvStructInfo置为null然后计算解码所需的buffer大小,随后传递给SPExternalAlloc申请内存。
第二次CryptDecodeObject调用之前,pvStructInfo为0,cbEncoded指向加密的内容。
随后,两次调用memcpy,第一次memcpy只是复制了解密之后的直接长度,这个数值;
第二次memcpy调用才真正复制内容。
那么,发生的heapoverflow就一定发生在第二次memcpy的时候。
我们来看下,是否能控制第二次memcpy的长度,或者让之前分配内存的时候分配得小一点。
Heapoverflow
经过参数追溯,可以这两个数据是由上一层函数传入。
其中,第二次memcpy的长度是由这个结构中直接读出来的,
而目的地址的内存大小是根据整个签名结构的头部描述的总长度计算出来。
头部的total size会先被除以8,然后乘2,计算所得的长度用来申请第二次memcpy的目的地址大小。
当然这个结构只是在解密签名时候的中间数据,并不是最初的签名文件数据。
如果对应到发送网络数据包中的样子,大概如下。
sig[1] = cbEncoded 即头部,total size;
sig[2] = \x30 这个好像是判断数据类型,不能变,否则改变程序分支
sig[3] = (sig[1] -1) memcpy的长度字段,改得越大越好,但如果超过\x7f就会报错
sig[4] = \x02
sig[5] = 1
sig[6] = 0 数据随意,这里设置为0
sig[7] =\x02 同样,代表数据类型
sig[8] = sig[1]-7 = \x7a
sig[9]…sig[x] 这就是heapoverflow之后填充的数据了
如此发送数据,最终触发漏洞,crash
在堆中,溢出覆盖的内存如下:
目前为止,poc可达到的效果即稳定堆溢出。不过溢出覆盖的代码不确定,且没有稳定布置内存的手段,仅仅通过网络报文似乎很难操纵被溢出的数据,无法达成远程代码执行的效果。
溢出之后发生crash的时间也不一定,主要由那段数据什么时候被用到决定。
结语
Windows在解密签名的时候经过的计算逻辑非常复杂,当然签名以及数据包本身的结构也很复杂。当代码相信了用户数据中对长度的描述而并没有检查,很自然的恶意构造的通信包就可以造成windows代码的堆溢出。同时,这段代码又运行在内核权限,这样就造成了如今扑朔迷离又影响甚广的winshock漏洞。
(本文来源于:CodeSec,原文链接:http://www.codesec.net/view/53918.html)
|