程序编译及测试的环境均与前例相同,除了在此使用了Unicode字符集及打开了缓冲区安全检查选项(/GS),我们在此继续使用托管扩展(CLR)。
这是一个非常简单的程序,尽管为了支持Windows GUI,它显得稍微有点长。在17至20行,有几个有意思的变量,lpszPassword是一个由16个宽字符(32字节)组成的已初始化的静态变量,紧跟其后的是userP指针及两个无符号整形:userNameLen和userPasswordLen,之后,userP在33行初始化。这些变量的地址如下:
| &lpszPassword = 0x0040911C &userP = 0x0040913C &userNameLen = 0x00409140 &userPasswordLen = 0x00409144 |
userP的值为0x00554D30,userNameLen的值为0x00000010,userPasswordLen的值为0xffffffff。如果我们查看lpszPassword地址的起始处内存,可以非常清楚地看到这些变量的初始值(见插3)。
代码段3:
| 0040911C 30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 0040912C 38 00 39 00 61 00 62 00 63 00 64 00 65 00 00 00 0040913C 30 4d 55 00 10 00 00 00 ff ff ff ff 8a 00 07 02 0040914C c6 00 07 02 02 01 07 02 00 00 00 00 01 00 00 00 |
此程序中的漏洞是在118至123行中对SendDlgItemMessage的调用,EM_GETLINE消息指定了从编辑控件IDC_EDIT1获取一行文本--编辑控件在Login对话框中,并把它复制到定长缓冲区lpszPassword中。这个缓冲区只能容纳15个Unicode字符及一个结尾的null,如果输入了多于15个字符,就会发生缓冲区溢出;在此假设输入了20个字符,第17及18个字符将会覆盖掉userP,第19及20个字符将会覆盖掉userNameLen,结尾的null将会覆盖掉userPasswordLen。
假定userP与userNameLen两者都被覆盖,当userNameLen被赋给存储在userP+4(user结构内len的偏移地址)的地址时,在124行就会导致对内存的任意写入。通过把一个地址覆盖为控制权最终要传递到的地址,攻击者就能利用内存的任意写入,把控制权传给任意的代码。而在本例中,堆栈上的返回地址被覆盖了。
因为lpszGuestPassword变量是一个声明在GetPassword函数中的自动变量,我们也可以查看这个变量地址起始处的内存。假定lpszGuestPassword定位在0x002DEB9C,那么可在这个位置查看堆栈的内容。经由程序调试,可以确定0x004f3a99的返回码位于堆栈上的0x002DEBD0处(见插4)。
代码段4:
| 002DEB9C 4e 00 43 00 43 00 2d 00 31 00 37 00 30 00 31 00 002DEBAC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 002DEBBC 1e df b4 bd 00 00 00 00 50 15 40 00 64 ec 2d 00 002DEBCC ec eb 2d 00 99 3a 4f 00 05 27 00 01 00 00 00 002DEBDC b0 32 2f 00 84 ec 2d 00 da c4 fc 79 58 f1 2d 00 |
| "1234567812345678\xebcc\x002d\x9028\x0040" |
| 0040911C 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00 0040912C 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00 0040913C cc eb 2d 00 28 90 40 00 00 00 ff ff 8a 00 07 00 0040914C c6 00 07 02 02 01 07 02 00 00 00 00 01 00 00 00 |
| 002DEB9C 4e 00 43 00 43 00 2d 00 31 00 37 00 30 00 31 00 002DEBAC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 002DEBBC 1e df b4 bd 00 00 00 00 50 15 40 00 64 ec 2d 00 002DEBCC ec eb 2d 00 28 90 40 00 0e 05 27 00 01 00 00 00 002DEBDC b0 32 2f 00 84 ec 2d 00 da c4 fc 79 58 f1 2d 00 |
| LRESULT Retval; *((WORD *)(&lpszPassword)) = (sizeof(lpszPassword)/sizeof(TCHAR))-1; Retval = SendDlgItemMessage(hDlg, IDC_EDIT1, EM_GETLINE, (WPARAM) 0, // line 0 (LPARAM) lpszPassword ); lpszPassword[Retval]='\0'; |

