意外发现一个溢出漏洞

昨天下午在用OllyDbg分析sdemo2.0 缓冲区溢出漏洞时不小心将大约几千个字符”A”粘贴进了Command,OllyDbg随即运行出错, 点调试后惊奇地发现EIP被覆盖为41414141。又测试了几次,确定OllyDbg的插件CmdBar存在溢出漏洞。我的第一个漏洞就这样在意外中发现了!

一、漏洞重现

实验环境:Windows7旗舰版 Service Pack 1,OllyDbg1.10(汉化第二版)以兼容Windows Xp SP3 方式运行。WindowsXP下运行OllyDbg也有此漏洞。

复制粘贴250个字符“A”到Command中就会出现上图所示的错误提示,点击调试后会发现EIP被覆盖为0x41414141,如下图所示:

这是很典型的溢出漏洞。改变字符串长度,会发现当字符串长度小于等于238时不会有错误产生,当字符串长度大于等于250时EIP被覆盖为0x41414141,字符串长度为239到250时,错误各不相同,总结为下表:

“A”的个数 EIP 错误提示
<239 无错误
239 0xC02F0001 访问违规:正在执行[C02F0001]
240 0x00000000 访问违规:正在执行[00000000]
241 0x04CA2DFA 访问违规:读取[00002E36]
242 0x05062DFA 访问违规:读取[002E2234]
243 0x05062D00 访问违规:正在写入到[00000001]
244 0x0439002E 访问违规:正在写入到[00000001]
245 0x03002E22 访问违规:正在执行[03002E22]
246 0x002E2220 访问违规:正在执行[002E2220]
247 0x2E222041 访问违规:正在执行[2E222041]
248 0x22204141 访问违规:正在执行[22204141]
249 0x20414141 访问违规:正在执行[20414141]
≥250 0x41414141 访问违规:正在执行[41414141]

二、漏洞产生原因分析

来用OllyDbg调试OllyDbg,找出产生漏洞的代码。程序出错后选择调试,在栈中可以看到大量的“41”,以及一个注释——“CmdBar.05172C90”。(上图中是“CmdBar.03CF2C90”,但这次调试中是“CmdBar.05172C90”,这个值每次都可能不一样。)既如此,就把断点设在这里好了。试着运行一下,发现中断过于频繁,又注意到0x05172C90是一个函数的开始,它的前面是另一个函数,尝试将断点设在这个函数中,就设在0x05172C5F处好了。重启OllyDbg,打开OllyDbg.exe,按F9运行,再在0x05172C5F处设置断点,然后向被调试的OllyDbg的Command中复制粘贴超长字符串(250个”A”),程序中断:

可以看到<jmp.&USER32.SetWindowTextA>是要设置某个窗口的提示字符串。先不管它,继续按F8单步运行,直到retn:

可以看到返回地址被41414141覆盖,更准确的说是被第247、第248、第249和第250个”A”覆盖。这就显示出了用250个字符测试的好处,若太长,覆盖了栈中的“CmdBar.05172C60”的话,就没这么好调试了。但到目前为止只找到了EIP被劫持为41414141的关键指令retn,还没有找到产生漏洞的罪魁祸首。

向上翻阅反汇编代码,发现有一个repne指令和两个rep指令,在这三个指令处下断点,如下图所示:

重新来过,程序果然停在了repne处:

REPNE SCAS BYTE PTR ES:[EDI]
扫描 ES:[EDI]字符串中与 AL的值相同的字符,遇到相同的后停止
参考:REPNE SCAS BYTE PTR ES:[EDI] 指令详解

紧接着执行NOT ECX后ECX的值便是字符串长度0xFB(十进制的251,250个”A”和一个截止符)。继续单步运行,依次执行完两次rep,却发现返回地址并没有被覆盖,这一点与预想不同。继续按F8单步运行,当执行完<jmp.&USER32.wsprintfA>后,发现返回地址被覆盖。

问题出在这里:在调用函数wsprintfA时未检查参数字符串的长度,所以导致溢出。原来会复制一段内存的函数不仅有strcpy,strncpy和memcpy,还有wsprintfA这样的函数。

再检查一次吧。删除其他所有断点,仅在调用wsprintfA处下断点,重新来过,粘贴250个”A”后,程序停在了调用函数wsprintfA处,如下图所示:

按一下F8执行完wsprintfA后,位于0x0018F14C处的函数返回地址已经被覆盖:

可以确定,确实是调用函数wsprintfA时未检查参数字符串的长度导致的溢出漏洞。至此,这个漏洞的产生原因分析完成。下一步本该是写出攻击代码,但我想先写写总结。如果要写攻击代码,会在另一片博文中展示。

三、总结

这篇文章阅读起来大约只需十几分钟,但我却花了超过8个小时才完成它。中途走了很多弯路,刚开始想,既然是溢出漏洞,一定涉及到字符串的复制,在给strcpy和strncpy函数都下断点无果后,寻找rep指令,给所有rep指令下断点,耐心调试后发现

    - rep -  movs dword ptr es:[edi], dword ptr [esi]

将过长的字符串复制到了栈中。按下Shift+F2,在该处下条件断点:[ESI]==41414141。当向被调试的OllyDbg的Command中复制过长的字符串后,程序会停在刚刚设置的条件断点处。这时查看调用堆栈(Alt+K),发现调用的是memcpy函数,而不是strcpy或strncpy。用bp memcpy来设置新的断点,但用鼠标一点Commamd中断就会发生,无法向Command中复制超长字符串,且不知为何,设置条件断点也没能成功。所以换个思路,超长字符串是复制粘贴进去的,所以用bp GetClipboardData来设置断点。

重启OllyDbg,打开OllyDbg.exe,按F9运行,再用bp GetClipboardData设置断点,然后向被调试的OllyDbg的Command中复制粘贴超长字符串,程序被中断在GetClipboardData函数的第一条指令处,之后按Ctrl+F9运行到返回,可以看到此时EAX指向超长字符串,然后用bp memcpy设置断点,再按F9运行程序,如此反复尝试,也没有什么结果。

又想设置内存断点,但不知何种原因,内存访问断点是可以正常工作的,但内存写入断点总是无效的。尝试了好多后只能放弃。山穷水尽之时忽然看到“CmdBar.05172C60”,这才柳暗花明。

灵活多变的思路很重要,一路不通,则换一路,兵者,诡道也。经验也很重要,知道都有哪些路可走,甚至创造出新的路来。

明显的感觉到,自己在看完《0day安全:软件漏洞分析技术》后,对于简单的漏洞原理都是掌握了的,但真的拿到一个庞杂的软件,想从中定位几行有漏洞的指令,还是很困难的。我缺乏的,是调试的知识和经验。

四、看雪论坛的OllyDbg系列教程

  1. CCDebuger的OllyDBG入门教学
  2. OllyDBG技巧汇集
  3. OD脚本教学

2 Replies to “意外发现一个溢出漏洞”

发表评论

电子邮件地址不会被公开。 必填项已用*标注