缓冲区溢出分析第09课:MS06

前言

经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用。在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否定的。即便是打了补丁,虽说我们之前所分析的漏洞已被补上,但是这个程序中依旧存在着其它的问题。

对漏洞函数进行静态分析

我们之前所研究的Netapi32.dll的大小为309,008 字节,补丁后的文件大小为309,760 字节。我们用IDA Pro载入打过补丁后的DLL文件,找到之前出现问题的函数位置进行分析:

图1

补丁前的程序之所以会出现问题,就是因为程序并未对第二个参数的长度做出足够的限定。补丁前的程序在以宽字符的形式计算出第二个参数的长度之后,是与0x411进行比较的,也就是说,只要第二个参数的长度不大于0x822个字节就可以。但是在补丁后的程序中,如上图所示,在位于0x71BA42BC中的比较语句,是将第二个参数(Source)与0x207相比较,也就是说,此时第二个参数的大小不能够超过0x414个字节,而栈空间分配的就是0x414个字节,因此不会出现溢出的情况。所以如果想实现漏洞的利用,还需要进一步深入挖掘才可以。对此,我们可以从这个函数最开始的部分进行分析:

图2

程序在分配了0x414字节的空间之后,利用异或运算将esi中的内容清空,然后将[ebp+arg_0]与esi也就是0进行比较,其实这也就是在判定,指向第一个字符串的指针是否为空。如果为空,那么接下来的条件跳转不成立,程序就会来到0x71BA42AE的位置执行。如果指向第一个字符串的指针不为空,但是指向的却是空的字符串,见下图:

图3

这里对第一个字符串的长度进行测定,如果字符串内容为空,也就是长度为0,那么接下来的跳转成立,程序会直接来到0x71BA42B5的位置执行:

图4

程序在这里会将未初始化的缓冲区以及第二个参数(Source)利用wcscat函数进行连接。其实这是一个非常不安全的操作,因为尚未初始化的栈空间的长度是未知的,甚至可能超过起初分配的0x414个字节,然后再与参数二进行连接,就会有缓冲区溢出的隐患。

这里有一个问题需要说明的是,为什么第一个参数指向的字符串拷贝到缓冲区就不会有这个问题,而第二个参数复制进缓冲区就会有隐患呢?这是因为第一个参数字符串使用的是“wcscpy”进行拷贝的,它会将字符串从缓冲区的起始位置进行拷贝。而第二个参数使用的是wcscat,它会检查缓冲区中的“\0”也就是字符串的尾部位置,然后将新的字符串拷贝到“\0”的位置,最后再加上“\0”作为结尾。

那么现在的问题就是应该如何控制缓冲区的内容和大小。一个简单的做法就是连续调用两次CanonicalizePathName()函数。当第一次调用该函数时,缓冲区第一次被填充并进行字符串连接的操作,当第一次调用结束的时候,恢复栈帧,释放缓冲区空间,,此时只要不做任何的栈空间操作(比如函数调用),那么原始缓冲区中的内容是不会改变的。而第二次调用时,还会继续对这块缓冲区进行操作,那么此时就会还保留有第一次调用时的数据,于是就存在着溢出的风险。

函数CanonicalizePathName()是不能够被直接调用的,因为它是NetpwPathCanonicalize()的子函数。所以这里可以回到NetpwPathCanonicalize()中来看一下二者之间的调用方式:

图5

由上图可见,当CanonicalizePathName()调用完毕后,程序会对其返回值(保存在eax中)进行判断,也就是与edi相比较,而edi在这里是恒为0的,那么这里也就是在验证其返回值是否为0。如果不为0,那么就会跳到0x71BA4284处执行,也就是函数退出的位置。如果为0,那么接下来还会有其它的函数调用。刚才说了,只要没有其它的函数调用,那么缓冲区的内容就不会被更改,也就是说,我们这里希望CanonicalizePathName()的返回值不为0,让程序接下来直接退出,以保证缓冲区中的内容不被更改。那么该如何让它的返回值不为0呢,我们可以看一下该函数最后一部分的反汇编代码:

图6

坚韧是成功的一大要素,只要在门上敲得够久够大声,终会把人唤醒的。

缓冲区溢出分析第09课:MS06

相关文章:

你感兴趣的文章:

标签云: