目标程序: Internet DownloadManager(版本6.25.12.2)
MD5: 4D4DE41DA750649505E884CEC4A1422F
测试环境: Windows 7 SP1 x86
分析工具:
OllyDbg;PEID;010Editor;ImmunityDebugger等
0x01简介
Internet Download Manager,简称IDM,是国外的一款优秀下载工具,该软件同时是一款共享软件。
通过IDM> Downloads > Find (或直接ctrl+F)打开Find file窗口后,在文本框内贴一串超长字符串,如下图所示,然后点击查找,程序发生崩溃。
0x02漏洞成因
用PEID查看,发现这是Microsoft Visual C++ 6.0开发的Windows 32位系统的应用程序,如下图所示,初步判断应该是缓冲区溢出导致的崩溃。
首先运行程序IDMan.exe,在Downloads > Find的文本框内贴一串超长字符串,再用OD附加该进程,下函数断点(bpGetWindowTextW),该函数功能是将指定窗口的标题条文本拷贝到一个缓存区内,继续F9运行,再点击find按钮,程序会断在USER32.GetWindowTextW函数的入口地址处。利用栈回溯的方式可以找到函数GetWindowTextW被调用的位置,如下图所示。在这之前程序分配了大小为0x1000字节的栈内存(0x00BC8730-0x00BC9730),用于存放从外部拷贝的字符串。
函数原型:Int GetWindowTextW(HWND hWnd,LPTSTR lpString,In nMaxCount )
hWnd:带文本的窗口或控件的句柄;
IpString:指向接收文本的缓冲区的指针,当前值为0x00BC8730;
nMaxCount:指定要保存在缓冲区内的字符的最大个数,其中包含NULL字符,当前值为0x1000(4096)。
在此之前程序整个过程是没有问题的,继续单步步入,发现程序是通过memcpy函数实现字符串拷贝的,如下图所示。
Memcpy函数要拷贝的字节数为0x1FFE,远远大于之前所分配的栈内存大小(0x1000字节),因此造成缓冲区溢出,引起程序崩溃。
为什么memcpy函数拷贝的字节数比分配的栈内存空间大这么多呢?
找到存储在程序内存中的外部输入字符串,发现数据均以Unicode方式存储!!问题就出在这个地方。
程序为外部输入字符串分配了大小为0x1000字节的栈内存;函数GetWindowTextW要拷贝的字符数最大值是0x1000个,如果拷贝的字符数超过的最大值,则只会拷贝少于这个最大值的字符数,并且会以null结束。但是函数GetWindowTextW会把从外部读取的字符以Unicode编码的方式存储,因此在调用memcpy函数时,要拷贝的字节数便成了0x1FFE((0x1000-1)*2)字节。
0x03漏洞利用
由于memcpy函数实际拷贝的字节数超过程序所分配的栈空间大小,导致发生栈溢出,覆盖了函数的返回地址0x00BC9730,如下图所示。查看当前各寄存器,发现寄存器EDI、ESP、EBP均指向用户可控的缓存区地址,因此只需用跳转指令覆盖返回地址,跳转到EDI或ESP或EBP所指向的缓存地址,就实现了漏洞利用。
用Immunity Debugger加载程序,执行程序到如图5的地方,利用其插件mona查找程序中的JMP EDI,CALL EDI, PUSH EDI等指令(因为没有找到符合条件的ESP操作指令)。因为用户输入字符到程序内存后是以Unicode方式存储的,比如,传入字符为123456,程序读入内存中为0x003100320033003400350036,所以只有指令地址为0x00XX00XX这种形式的,才能被利用。以下是找到的所有符合条件的EDI寄存器操作指令,如下图所示。
选择其中一个地址(以0x00470057为例),用以覆盖返回地址,当程序执行返回操作后,结果如下图所示。
如下图所示,为程序跳转到栈地址(0x00BC9CCC)执行,获得程序控制权,只要在此处布置shellcode即可执行想要的操作。
0x04小结
此漏洞为程序开发者在开发过程中,未考虑到字符传入内存后以Unicode方式存储,导致在预先分配的内存空间不足,从而产生的缓冲区溢出漏洞。在构造利用代码的时候注意传入字符会转为Unicode方式存储或执行。