SafeSEH利用(DEP/ASLR disabled)

最近在看《0day安全:软件漏洞分析》那本书,初步接触一些windows下的溢出利用,和linux上还是有较大不同的。

本篇对应书上第6、11章节关于利用SEH异常处理来绕过GS的内容。

GS相当于Windows下的canary,这时通过溢出覆盖返回地址,来控制程序执行流程的方法就不再可行。在linux下绕过canary的方法也有一些,在windows下总的思路是一致的:

一、泄露canary的值(参考 %s打印栈上数据时的canary泄露)

二、canary检查的机制是生成随机canary后放到返回地址之前,并将其备份(例如linux下存到gs:0x14),在函数返回时,再把两处的canary通过异或来判断是否发生改动,如果能够同时修改这两个地方的canary值,就可以通过canary的检测。(难以利用)

三、利用其它溢出方式(虚函数,堆)。

以上三种比较通用,除此之外,linux下还有一种利用___stack_chk_fail()函数实现一次任意地址读的姿势(Stack Smashing Protector任意地址读)

而windows下还可以利用覆盖SEH异常处理指针,只要触发异常先于canary检查,就可以先去执行SEH异常处理函数从而实现利用。

SEH chain结构:

SafeSEH利用(DEP/ASLR disabled)_第1张图片

由pointer to next SEH构成单向链表,而handle指向异常处理函数。

具体的异常处理机制就不做过多说明。

Windows XP sp2之后引入了SafeSEH,接下来的内容主要针对SafeSEH,且暂时不考虑DEP。

SafeSEH针对指向异常处理函数的指针做了若干检查,使得我们直接覆盖其为shellcode地址变得不可行。先看看能够通过检查的情况:

1.异常处理函数位于加载模块内存范围之外,DEP关闭

2.异常处理函数位于加载模块内存范围之内,相应模块未启用SafeSEH,同时相关模块不是纯IL指令。

3.异常处理函数位于加载模块内存范围之内,相应模块启用SafeSEH,异常处理函数地址包含在安全S.E.H表中。

0x00 利用未启用SafeSEH模块绕过SafeSEH

实验环境:

     Windows XP SP3    (DEP disabled)

     Visual Studio 2008

     VC++ 6.0


首先用vc6.0构建一个未启用SafeSEH的dll:

#include "stdafx.h"

#include "stdio.h"

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )

{

    return TRUE;

}

void jump()

{

    puts("hereisit");         //主要用于定位ROP

    __asm{

            pop eax

            pop eax

            retn                     //一个pop pop ret 的rop,用法后面会提到

                 }

}

为了防止ROP的地址包含‘'\x00'被截断,需要手动设置dll的加载基址:

在 工程-->设置-->连接 的工程选项中添加  /base:"0x11120000"/

接着用vs2008构建一个启用SafeSEH的exe,其中调用之前的dll。(vs2008默认开启GS、SafeSEH)

char shellcode[]="”;

DWORD MyException(void)

{

         printf("This 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(shellcode);

           return 0;

}

SEH_NOSafeSEH_JUMP.dll

test.exe

先用Immunity Debugger的mona插件计算需要多少个字节才能覆盖到SEH的内容:

把生成的字符串放到shellcode数组中,od或者immunity debugger调试,此时SEH的pointer to next SEH被覆盖为41326841,由于是小端格式,倒过来用mona计算偏移:

SafeSEH利用(DEP/ASLR disabled)_第2张图片

我们只需在shellcode中填充’\x90'*216,接着就将覆盖SEH结构体。

栈中内容大致是这样的

SafeSEH利用(DEP/ASLR disabled)_第3张图片

调用异常处理函数的过程:

把0x12FE94处的四字节放入eax,然后call eax,这时栈的情况:

SafeSEH利用(DEP/ASLR disabled)_第4张图片

回看之前我们的rop:POP POP RETN,两次弹栈后将执行0x12FE90处的指令。

书中给出的shellcode布局是:

SafeSEH利用(DEP/ASLR disabled)_第5张图片
做图的时候有点问题。。。这块应该是buffer的栈上数据

执行了我们伪造的异常处理函数,实际上是pop pop retn后,eip指向0x12fe90,\x90是nop指令,pop pop retn的地址相当于一个操作eax的指令无关紧要,那么可以一直滑向shellcode执行。

但是实际调试时,这样布局0x12ef98~0x12fe9c的位置会被破坏,不再是NOP指令无法滑向shellcode。

由于没有开启DEP,我在0x12FE90的位置放置了\xeb\x0b\x90\x90,对应汇编指令jmp 0x10,直接跳到0x12fea0即shellcode的第一条指令执行。

SafeSEH利用(DEP/ASLR disabled)_第6张图片
成功执行弹窗shellcode

0x01 利用加载模块之外的地址绕过SafeSEH

当程序加载到内存中后,在它所占的整个内存空间中,除了我们平时常见的PE文件模块(EXE和DLL)之外,还有其他一些映射文件,例如,类型为MAP的映射文件,SafeSEH是无视它们的,所以我们可以在这些文件中寻找跳转指令。

这种情况下可用的跳板地址,除了pop pop retn序列外,还有:

call / jmp dword ptr [esp+0x8/0x14/0x1c/0x2c/0x44/0x50]

call / jmp dword ptr [ebp+0xc/0x24/0x30]

call / jmp dword ptr [ebp-0x4/0xc/0x18]

关于为什么这些跳板可以,有兴趣的童鞋可以自己调试跟一下。

书作者开发了一个插件叫做OllyFindAddr,与ollydbg自己的指令搜索不同,该插件不只在加载模块中搜索指令,而是在整个程序的内存空间搜索。


SafeSEH利用(DEP/ASLR disabled)_第7张图片

exploit!

SafeSEH利用(DEP/ASLR disabled)_第8张图片

后记

某次课堂练习复现一个简单的SafeSEH利用,系统是windows xp sp3 pro,od给的exp里不知道为什么要往seh handler填一个jmp ebx的地址,重新找了pop pop ret填入发现无法正常调用异常处理函数,最后发现根本找不到未开启safeSEH的模块。

然而发现exp中的jmp ebx是可以执行的,其位置位于地址空间的最后,一块未识别的模块。在其中搜索pop pop ret找不到,最后终于找到形如:

pop 

mov ebx,

pop

mov ebx,

ret

的指令序列,完成利用。

你可能感兴趣的:(SafeSEH利用(DEP/ASLR disabled))