异常程序
//******有漏洞的程序************* //漏洞函数VerifyPassword 由于文件大小是可变的 但是szBuffer大小只有50 当文件大小大于50的 //时候就会产生缓冲区溢出的异常 我们就可以利用这个异常溢出到函数返回地址 达到执行我们代码的目的 #include<windows.h> #include<tchar.h> #include <iostream> #define PASSWORD "1234" void* VerifyPassword(byte* pRet, void* pszPassword, int nSize) { char szBuffer[50] = { 0 }; *pRet = strcmp(PASSWORD, (char*)pszPassword); return memcpy(szBuffer, pszPassword, nSize); //通过拷贝溢出 } int _tmain(int argc, _TCHAR* argv[]) { void* pszPassword = NULL; int nFileSize = 0; byte bFlag = FALSE; FILE* fp; LoadLibraryA("user32.dll"); if (fopen_s(&fp, "password.txt", "rb")) { MessageBoxA(NULL, "打开文件失败", "error", NULL); return 0; } fseek(fp, 0, SEEK_END); nFileSize = ftell(fp) + 1; rewind(fp); pszPassword = ZeroMemory(malloc(nFileSize), nFileSize); fread(pszPassword, nFileSize, 1, fp); VerifyPassword(&bFlag, pszPassword, nFileSize); if (bFlag) printf("密码错误\n"); else printf("密码正确\n"); fclose(fp); system("pause"); return 0; }
vs关闭此设置
在之前的漏洞利用上 都是通过缓冲区溢出覆盖函数的返回地址 从而执行我们自己的shellcode,但是用户程序在开启GS安全检查以后,此方法就失效了
当用户开启GS安全检查以后 函数的开始就会有这样的防范代码
0x00401006 安全cookie(一个随机的值)赋值给eax,在和ebp进行异或 最后保存在ebp-4这个局部变量里
函数的结尾 再次从ebp-4这个地址取出异或后的值 在和ebp进行异或 如果堆栈是平衡的 此次异或后的值就要等于函数开始异或的值 接着会调用函数进行判断 如果值不同了 说明堆栈存在问题 就会退出程序 相同就会继续执行
检查cookie的函数 不相等就跳转 相等就直接返回正常运行
用户开启GS安全检查就无法使用缓冲区溢出覆盖返回地址这个操作了
可以通过栈溢出 让程序产生异常 覆盖异常处理函数的方式 去执行我们的代码
构建password.txt文件 是我们的有效地址精准的覆盖到[0019ff08]上
接下来就是要把8888替换成一个有效的地址 测试触发异常后是否会调用我们覆盖后的地址 触发异常通过栈溢出 像文件中加入大量的A 撑爆栈空间
在函数KiUserExceptionDispatcher下断点 函数产生异常就会调用此函数 接着进入这个函数
标黑函数会检查 异常处理函数是否在主模块中.......... 进入标红语句
调用异常处理函数
进入我们覆盖后的异常处理函数 查看参数第二个参数就是我们seh链的下一个seh节点的地址 就可以找到如下代码 pop ?? pop ?? ret 执行19ff60
在构建password.txt文件的时候 要构造如图的代码 16ff68就可以放shellcode的代码了
代码执行到19ff60 要跳过19ff64
也就是在文件中19ff60这个地址 在构造password文件的时候直接19ff60的位置就写eb06
19ff68直接写shellcode即可
总结:
因为开启了GS安全检查,所以老方法通过缓冲区溢出覆盖返回地址的方法行不通了,因为在函数返回前会检查cookie的值是否和刚进来异或后的值相等 我们全部覆盖会导致函数结束前再次异或的值不等于开始的值 函数直接异常退出
那么该如何解决这个问题哪????
就是利用栈溢出造成异常 系统调用异常处理函数 我们构造的文件正好覆盖异常处理函数的地址 又因为系统在调用系统异常处理函数的时候倒数第三个参数就是指向下一个seh节点的地址也就是下图的0x0019ff60 那我们就可以把19ff64里面的地址指向pop? pop? ret这样的代码
因此会把19ff60里面的数据当代码执行 19ff60写代码跳过19ff64 在19ff64下一句写shellcode就可以了