Windows平台下的堆溢出利用技术(二)(上篇)

lxj616 · 2014/05/06 16:27javascript

0x00 背景


开头我讨论了在旧版本Windows下利用堆溢出的技术,试着给读者提供一些关于unlink过程是怎样执行的、以及freelist[n]里面的flink/blink是如何被攻击者控制并提供简单的任意“4字节写”操做 的 实用的知识。html

本文的主要目的是从新提醒本身(我很健忘)而且帮助安全专家获取对老版本windows(NT v5 及如下)堆管理器如何工做 技术上的理解。本文完成的是利用堆溢出或者内存覆写漏洞 而且绕过特定的防止“4字节写”的混合措施。本文的目的还有让读者掌握基础的知识 其毫无疑问地在攻击新版本windows堆实现方式时被用到。前端

这个教程将会从细节上讨论一个而且仅讨论一个 为人熟知的针对特定程序绕过Windows XP SP2/SP3堆保护机制的技术。所以,这并非一个完善的入门书,它也不会涉及到堆的每个方面。java

继续以前,须要对windows XP/Server 2003下堆的构造有比较全面地理解。为了本教程自己的目的,以及基于文章Heap Overflow for Humans 101的反馈,我将会讨论一些在当前环境 堆内部如何工做的话题。python

若是在基础层面你对基于堆的缓冲区溢出不熟悉,那么建议你先把注意力放在这个地方。为了继续你须要:c++

•   Windows XP 仅安装SP1
•   Windows XP 仅安装SP2/SP3
•   一个调试器 (Olly Debugger, Immunity Debugger, windbg with access to the ms symbol server etc).
•   一个C/C++编译器 (Dev C++, lcc-32, MS visual C++ 6.0 (若是你还能找到它)).
•   随意一种脚本语言 (我用的python,也许你能够用perl)
•   A brain (and/or persistance).
•   一个大脑 (和/或 毅力)
•   一些汇编和C的知识,以及如何用调试器 调试的知识
•   OD的插件HideDbg或者 !hidedebug under immunity debugger
•   时间
复制代码

拿杯咖啡而后咱们一块儿来调查这种黑暗艺术。git

因此到底什么是chunk和block?github

Chunk是一种简单的用block衡量的连续内存 而且其特定的状态取决于特定的堆chunk header中flag的设定。Block是简单的一块8字节堆空间。通常地咱们关心一个chunk是被分配过仍是被free掉了。在本文中为了方便叙述,这些名词都会以这种含义使用。spring

无论怎么说,全部的chunk都存储在堆中。额外地,堆chunk也许会出如今堆结构的其余位置而且取决于其位置会有不一样的chunk结构。shell

让咱们经过讨论堆结构自己以及三个很是重要的堆中chunk存储结构开始吧。

0x01 堆的结构


默认状况下,Windows有一个特定的结构供堆参考。在PEB中的0x90偏移,你能够看到 从堆建立位置起 以有序数组结构展示的 对应进程的堆列表 使用Windbg咱们能够经过!peb命令找到目前的PEB。若是你是Immunity Debugger的用户,你也能够经过!peb命令观看这条信息,!peb提供的很是简单的代码以下所示:

#!cpp
from immlib import *
def main(args):
        imm = Debugger()
        peb_address = imm.getPEBAddress()
        info = 'PEB: 0x%08x' % peb_address
        return info
复制代码

咱们一进入PEB就能看到进程的堆信息:

+0x090 ProcessHeaps : 0x7c97ffe0 -> 0x00240000 Void 
复制代码

因此让咱们在这个地址0x7c97ffe0 dump DWORD(4字节)

0:000> dd 7c97ffe0 
7c97ffe0 00240000 00340000 00350000 003e0000 
7c97fff0 00480000 00000000 00000000 00000000 
7c980000 00000000 00000000 00000000 00000000 
7c980010 00000000 00000000 00000000 00000000 
7c980020 02c402c2 00020498 00000001 7c9b2000 
7c980030 7ffd2de6 00000000 00000005 00000001 
7c980040 fffff89c 00000000 003a0043 0057005c 
7c980050 004e0049 004f0044 00530057 0073005c 
复制代码

加粗字体的地址就是目前在运行进程的堆。这条信息也能够在windbg和Immunity Debugger中经过使用!heap命令找到

额外地,你能够经过windbg中的!heap –stat命令观看每一个堆的静态信息。下面是示例回显:

_HEAP 00030000 
Segments 00000002 
Reserved bytes 00110000 
Committed bytes 00014000 
VirtAllocBlocks 00000000 
VirtAlloc bytes 00000000 
复制代码

最终,你能够经过使用-h和-q标签dump一些堆的元数据

第一个堆(0x00240000)是默认的堆,其余堆都是由C组件及构造方法建立的,而回显中最后的堆(0x00480000)是被咱们的程序建立的。

程序可能会调用形如HeapCreate()的函数来建立额外的堆而且把指针存储在PEB中0x90偏移量中,下例展现了HeapCreate()的Windows API

#!cpp
HANDLE WINAPI HeapCreate(
 __in DWORD flOptions,
 __in SIZE_T dwInitialSize,
 __in SIZE_T dwMaximumSize
 );
复制代码

一个带有正确参数的HeapCreate()调用将会返回一个存储在EAX寄存器中指向新建立的堆的指针

参数以下所示:

  1. flOptions

    • HEAP_CREATE_ENABLE_EXECUTE: 容许执行代码 • HEAP_GENERATE_EXCEPTIONS:当调用HeapAlloc() 或者 HeapReAlloc()时没法知足需求状况下抛出一个异常 • HEAP_NO_SERIALIZE:当堆函数访问堆时不使用序列化方式

  2. dwInitialSize

为堆分配的初始大小是最近的页表大小(4K)。若是指定为0,那么就给堆设置了一个单页大小。这个值必须比dwMaximumSize小

  1. dwMaximumSize

堆的最大大小。若是对HeapAlloc() 或者 HeapReAlloc()的请求超出了dwinitialSize的值,那么虚拟内存管理器将会返回 可以知足分配请求的内存页表 而且剩下的内存会被存储到freelist中

若是dwMaximumSize为0,堆大小可以自动增长。堆的大小只被可用内存限制

更多关于flag的信息能够查阅msdn。下面是一张重点区域高亮显示的堆结构表。你能够在windbg下使用命令'dt _heap'来查看此条信息。

Address

Value

Description

0x00360000

0x00360000

Base Address

0x0036000C

0x00000002

Flags

0x00360010

0x00000000

ForceFlags

0x00360014

0x0000FE00

VirtualMemoryThreshold

0x00360050

0x00360050

VirtualAllocatedBlocks List

0x00360158

0x00000000

FreeList Bitmap

0x00360178

0x00361E90

FreeList[0]

0x00360180

0x00360180

FreeList[n]

0x00360578

0x00360608

HeapLockSection

0x0036057C

0x00000000

Commit Routine Pointer

0x00360580

0x00360688

FrontEndHeap

0x00360586

0x00000001

FrontEndHeapType

0x00360678

0x00361E88

Last Free Chunk

0x00360688

0x00000000

Lookaside[n]

0x02 堆 segments:


正如以前所提到的,每一个堆chunk都被存储在堆segment里面。若是一块内存chunk被free了,它除了将会被加入freelist或者lookaside list以外还会被存储到堆segment里。当分配空间时,若是堆管理器在lookaside或者freelist里找不到任何的空闲chunk,它就会从未分配内存中提供更多的内存给当前的堆segment。若是大量内存被分配到不少地方,堆的结构能够拥有许多segment。下面显示的是segment chunk结构表:

Header

Self Size (0x2)

Prev Size (0x2)

Segment index (0x1)

Flag (0x1)

Unused (0x1)

Tag index (0x1)

Data

在分析堆segment时,咱们能够经过在windbg中使用'!heap -a [heap address]'命令完成

额外地,你能够在immunity debugger中使用 '!heap -h [heap address] -c'(选项-c 显示chunk)

每一个segment在数据chunk后面包含了它本身的元数据。下面是segment分配的内存,而且最终这个segment包含了一个未分配内存的部分。如今咱们清楚了咱们想要分析的segment,咱们可使用命令'dt _heap_segment [segment address]'来dump元数据结构。

下面是一个详细的元数据结构表单。为简单起见,咱们将设定从地址0x00480000开始。

Address

Value

Description

0x00480008

0xffeeffee

Signature

0x0048000C

0x00000000

Flags

0x00480010

0x00480000

Heap

0x00480014

0x0003d000

LargeUncommitedRange

0x00480018

0x00480000

BaseAddress

0x0048001c

0x00000040

NumberOfPages

0x00480020

0x00480680

FirstEntry

0x00480024

0x004c0000

LastValidEntry

0x00480028

0x0000003d

NumberOfUncommitedPages

0x0048002c

0x00000001

NumberOfUncommitedRanges

0x00480030

0x00480588

UnCommitedRanges

0x00480034

0x00000000

AllocatorBackTraceIndex

0x00480036

0x00000000

Reserved

0x00480038

0x00381eb8

LastEntryInSegment

在一个segment中重要的信息是第一个chunk。单这条信息就能够用来"遍历"segment,如是你能够简单地 经过知道大小及间隔 显示chunk而且下一个chunk

0x03 后端分配器 - Freelist:


在堆结构的0x178偏移量上,咱们能够看到FreeList[]数组的开头。这个FreeList包含了一个双向连接的chunk list。他们经过既有flink又有blink的方式来双向连接。

上面的图表显示了freelist包含着范围在0-128的堆chunk索引数组。任意chunk的大小在0和1016之间(之因此是1016是由于最大1024减去8字节的元数据)并存储在[它们的分配大小]*8位置。举例来讲,我有一个40字节大小的chunk来free,那么我将会把这个chunk放在freelist中index为4的位置(40/8)

译者注:40/8=5 ;0,1,2,3,4 因此放在4

若是一个chunk的大小超过了1016(127*8)个字节,那么它将会以数值大小顺序被存储在freelist[0]条目中。下面是关于freelist chunk的描述

Headers

Self Size (0x2)

Prev Size (0x2)

Segment index (0x1)

Flag (0x1)

Unused (0x1)

Tag index (0x1)

flink/blink

Flink (0x4)

Blink (0x4)

Data

Freelist Mitigations:

微软发布了一些mitigation来防止对于freelist条目的unlink攻击,下面是一段对mitigation简短的描述。

对freelist的Safe unlinking:

Safe unlinking 是被微软在Windows XP sp2及之后实现的一种保护机制。本质上它是一种防范被攻击利用[以前在Heap Overflows for Humans 101里说起的4字节写]的技术。在这种检测之下,以前的chunk 'flink'指针指向咱们分配的chunk而且临近的chunk指向咱们分配的chunk。下面是对于对应的安全机制的描述图表

Freelist chunk 1[flink] == Freelist chunk 2 && Freelist chunk 3[blink] ==Freelist chunk 2

红线是检查发生的地方

若是任何检查未经过,那么就jump到ntdll.dll的0x7c936934,正如你所见这几乎与咱们传统的unlink相同除了咱们加入了检查flink/blink的代码

Freelist header cookies:

对于Windows XP sp2的介绍中可见一个随机的堆cookie位于chunk header中的0x5偏移。。只有freelist chunks有这些cookie检查。下面是一张加亮了安全cookie的关于堆chunk的图片。这是一条随机单字节条目,这样有最大256种可能的值。记住你在一个多线程的环境下也许可以暴力穷举这个值。

0x04 前端分配器-Lookaside:


Lookaside list是一个用来存储小于1016字节堆chunks的单链表。Lookaside体现的理念是加速而且查找起来更快。这是由于程序在进程的执行生命期内屡次执行HeapAlloc() 和 HeapFree()。由于它是为速度和效率设计的,因此它容许了每一个条目很少于3个的空闲chunk。若是HeapFree()在一个chunk上调用而且已经有了3个条目的特定chunk大小,那么它就会被free到Freelist[n]

堆的chunk大小总计是 实际分配大小+用来header的额外的8字节。因此若是若是为16字节作分配,那么就会在lookaside list里面扫描24字节(16+chunk header)大小的chunk。在下图的状况下,windows堆管理器会成功而且在lookaside list中index 2找到一个可用的chunk。

Lookaside list 仅包含了一个flink指针指向下一个可用的chunk(用户数据)

Headers

Self Size (0x2)

Prev Size (0x2)

Cookie (0x1)

Flags (0x1)

Unused (0x1)

Segment index (0x1)

flink/blink

Flink (0x4)

Data


当windows堆管理器接收到一个分配的请求,它会去寻找一个空闲的堆内存chunk来知足这个请求。

为了最优化以及速度,windows堆管理器将会首先进去lookaside list中(取决于它的单链表结构)而且试着去寻找一个空闲的chunk。若是这里没有找到chunk,windows堆管理器将会遍历freelist(在 freelist1-freelist[127]之间)。若是没有找到任何chunk那么它将会遍历freelist[0]条目寻找更大的chunk而后分割chunk。一部分将会被返回到堆管理器而且剩下的将会返回到freelist[n] (n是基于剩下字节数的序号)。这就带领咱们去往下一个章节,堆的操做。

0x05 堆操做基础:


Chunk分割

Chunk分割指的是在为寻找大小合适的chunk并将其分解成更小的chunk时访问freelist[n]的过程。当freelist中获取到的一个chunk比请求分配的大小要大时,这个chunk将会被对半分开来知足请求的分配大小。

假设在freelist[0]是一个单独的2048字节的chunk。若是请求的分配大小是1024字节(包括了header),那么这个chunk会被分割而且有1024字节大小的chunk放回了freelist[0] 同时返回新分配的1024字节的chunk给调用者。

Heap Coalescing:

堆的合并:

堆的合并指的是将两块可用的chunk堆内存合并的动做,当中间的chunk也被free了时。

堆管理器要这样作的缘由是对segment内存实行高效使用。固然,这也权衡了当chunk被释放时的效率。堆的合并是很是重要的操做由于多个chunk能够被加在一块儿(若是可用)而且以后能够被用于更大大小需求的分配。若是没有这个过程,那么在堆segment中浪费的chunk就会出现而且成为碎片。

0x06 绕过Windows XP sp2/3 安全机制的技术:


覆写lookaside中的一个chunk

这个技术是最通用的绕过堆cookie以及safe unlinking检查的方法。同时(译者注:原文为whist,想必做者原意whilst打错了)这是一个针对程序的达成简单的"4字节写"的技术,一些程序可能容许你决定堆的布局以稳定exploit利用。既然在lookaside list中没有safe unlinking 或者 cookie检查,一个攻击者能够覆写 包含在临近lookaside条目中 的'flink'的值 而且经过HeapAlloc() 或者 HeapFree()调用返回那个指针 以后在下一个可用chunk中写入恶意代码。

让咱们从视觉上感觉它是怎样工做的,深呼吸。

  1. 咱们经过在当前segment中分配chunk A开始

  1. 下一步咱们在相同segment分配另外一个chunk B

  1. 如今咱们free chunk B 去向lookaside list,那么有两个条目存在,一个在segment而另外一个在lookaside list。.

  1. 如今咱们溢出chunk A(会在之后溢出到chunk B中而且覆写其flink)。这是将会被覆写的元数据.

  1. 如今咱们从新再一次分配B(经过分配一个和B相同大小的chunk如第二步)。这会返回chunk B的指针而且更新其在当前堆segment中的引用而且准备好下一次的分配。如今chunk B 中的flink已经更新成攻击者经过溢出A而控制的任意地址。

  2. 如今咱们经过分配chunk C 获取控制。它将会是在chunk B以后的下一个可用chunk而且经过chunk B 的已经控制的flink 被指向了。攻击者在chunk C 填入他们的shellcode

当这些步骤所有完成,咱们如今控制了一个覆写的函数指针,其将会理想地在咱们的"4字节写"以后被调用。下面是咱们将会说明的C代码:

#!cpp
/*
        Overwriting a chunk on the lookaside example
*/
#include <stdio.h>
#include <windows.h>
int main(int argc,char *argv[])
{
        char *a,*b,*c;
        long *hHeap;
        char buf[10];

        printf("----------------------------\n");
        printf("Overwrite a chunk on the lookaside\n");
        printf("Heap demonstration\n");
        printf("----------------------------\n");

        // create the heap
        hHeap = HeapCreate(0x00040000,0,0);
        printf("\n(+) Creating a heap at: 0x00%xh\n",hHeap);
        printf("(+) Allocating chunk A\n");

        // allocate the first chunk of size N (<0x3F8 bytes)
        a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk B\n");

        // allocate the second chunk of size N (<0x3F8 bytes)
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+) Chunk B=0x00%x\n",a,b);
        printf("(+) Freeing chunk B to the lookaside\n");

        // Freeing of chunk B: the chunk gets referenced to the lookaside list
        HeapFree(hHeap,0,b);

        // set software bp
        __asm__("int $0x3");

        printf("(+) Now overflow chunk A:\n");

        // The overflow occurs in chunk A: we can manipulate chunk B's Flink
        // PEB lock routine for testing purposes
        // 16 bytes for size, 8 bytes for header and 4 bytes for the flink

        // strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x20\xf0\xfd\x7f");
        // strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBBDDDD");

        gets(a);

        // set software bp
        __asm__("int $0x3");

        printf("(+) Allocating chunk B\n");

        // A chunk of block size N is allocated (C). Our fake pointer is returned
        // from the lookaside list.
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk C\n");

        // set software bp
            __asm__("int $0x3");

        // A second chunk of size N is allocated: our fake pointer is returned
        c = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+)Chunk B=0x00%x\n(+) Chunk C=0x00%x\n",a,b,c);

        // A copy operation from a controlled input to this buffer occurs: these
        // bytes are written to our chosen location
        // insert shellcode here
    gets(c);

        // set software bp
            __asm__("int $0x3");

        exit(0);
 }
复制代码

咱们之因此有好多asm("int $0x3");指令是为了在调试器中设置软件断点来暂停程序执行。其余选择的话,你也能够在调试器中打开编译后的二进制文件而后再每一个call下断点。在dev c++(it uses AT&T in-line assembly compiling with gcc.exe)中编译代码。当代码在调试器中执行触发第一个断点时让咱们来看一下。

咱们能够看到在segment 0x00480000有两个已分配的chunk大小为0x18.若是咱们减去0x8字节咱们还剩0x10即16个字节。让咱们来看一看lookaside而且观察咱们是否把chunk B free过去了

太棒了!因此咱们能够在lookaside看到咱们的chunk(差8字节指到header)。这个chunk被free到这个位置取决于其大小< 1016而且目前在lookaside中有<= 3 chunk 指定那个chunk size。

为了进一步确认,让咱们看一眼freelist而且观察发生了什么

好的 那么它看上去很直接,除了 一些在freelist[0]在建立segment时正常的一些分配外 没有其余条目。继续,咱们用一些0x41溢出chunk A到临近的chunk header。使用一样的数据,咱们将用0x44溢出临近chunk的flink.

太棒了 咱们能够看到咱们分配的0x00481ea8-0x8(chunk B)已经被攻击者的输入覆写了。咱们也能够看到lookaside条目包含着0x4444443c这个值,若是咱们把这个值加上0x8字节就会是0x44444444即咱们使用的确切值!所以在这一点上你能够理解你是怎样控制chunk B 的flink的 :)

一旦执行与chunk B(0x00481ea8-0x8)大小相同的分配,chunk B 的条目将被从lookaside3移除而且返回给调用者。注意额外的,header也一样被彻底控制了。

因此若是咱们仔细看看chunk A(0x00481e88)咱们能够看到这个chunk正在被使用由于flag设置成0x1(表示它状态busy)。下一个在(0x00481ea0)的chunk目前尚未被更新由于其仍然被free到lookaside中。

这时候,代码将会在READ操做上违反访问规则。当使用这种技术攻击一个程序时,咱们会把0x44444444用一个函数指针替换(虚假flink)。如今,当堆管理器建立了下一个分配的chunk,该程序将会在虚假指针处写入。咱们如今会分配chunk C而且用任意shellcode填充buffer。这里的这个想法是让咱们的函数指针在程序崩溃以前被调用(或者由于崩溃而调用)。我没在Heap Overflow for Humans 101中提到的一个很是聪明的技巧是 攻击者能够利用PEB全局函数指针(仅在XP SP1 以前)。可是在windows XP SP2 及更高版本,这些地址指针都是随机的。让咱们来看看这个,在把二进制文件装载进调试器时咱们首先看到:

咱们再作一遍:

注意以上两个PEB指针是不一样的。当触发一个异常时,异常处理器大体将调用ExitProcess(),其又会调用RtlAcquirePebLock()。这项操做管理的是在处理异常期间不准修改peb 而且当处理器结束运做,它将会经过调用RtlReleasePebLock()释放lock。另外,在这些函数中使用的指针并非W^X保护的而这意味着咱们能够在那个内存区域写入而且运行。这些函数每个都利用了静态指针与固定的相对的peb的偏移量。下面是RtlAcquirePebLock()函数而且正如你所见,FS:18 (peb)被移进了EAX。接着,全局函数指针被存储在0x30偏移量的地方。而将会被调用的函数'FastPebLockRoutine()'位于0x24偏移量位置。

7C91040D > 6A 18            PUSH 18
7C91040F   68 4004917C      PUSH ntdll.7C910440
7C910414   E8 B2E4FFFF      CALL ntdll.7C90E8CB
7C910419   64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
7C91041F   8B40 30          MOV EAX,DWORD PTR DS:[EAX+30]
7C910422   8945 E0          MOV DWORD PTR SS:[EBP-20],EAX
7C910425   8B48 20          MOV ECX,DWORD PTR DS:[EAX+20]
7C910428   894D E4          MOV DWORD PTR SS:[EBP-1C],ECX
7C91042B   8365 FC 00       AND DWORD PTR SS:[EBP-4],0
7C91042F   FF70 1C          PUSH DWORD PTR DS:[EAX+1C]
7C910432   FF55 E4          CALL DWORD PTR SS:[EBP-1C]
7C910435   834D FC FF       OR DWORD PTR SS:[EBP-4],FFFFFFFF
7C910439   E8 C8E4FFFF      CALL ntdll.7C90E906
7C91043E   C3               RETN
复制代码

下面咱们能够看到函数RtlReleasePebLock()直接在PEB中的全局偏移数组0x24偏移量位置调用了函数指针'FastpebUnlockRoutine()'

7C910451 > 64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
7C910457   8B40 30          MOV EAX,DWORD PTR DS:[EAX+30]
7C91045A   FF70 1C          PUSH DWORD PTR DS:[EAX+1C]
7C91045D   FF50 24          CALL DWORD PTR DS:[EAX+24]
7C910460   C3               RETN
复制代码

因此当RtlAcquirePebLock() 和 RtlReleasePebLock()过程在有异常发生时被调用,这代码将会无限的触发异常而且执行你的代码。不过你能够经过执行完shellcode而后修改指针地址到exit()修补peb来实现。

当前进程的线程数越多,随机化的强度就越弱(随机地址将被用于多个PEB)而且咱们将可以"猜解"当前PEB的地址。不过因为咱们没有一个可靠的函数指针以供咱们作简单的"4字节"覆写(一个通用函数指针)

有时一个程序可能会既在异常发生前又在另外一个windows library函数指针被调用时使用一个自定义的函数指针 而且这就能够用咱们的代码覆写那个指针而且执行shellcode。

特定指针攻击利用:

为了展现的目的,我将会演示经过固定的PEB全局函数指针在windows XP SP1覆写一个lookside的chunk。FastPEBLockRoutine()的地址为0x7ffdf020。如今简单地从注释中恢复下面这行

// strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x20\xf0\xfd\x7f"); 
复制代码

而后把下面这行注释掉

gets(a); 
复制代码

因此如今咱们将会溢出chunk A X's而且溢出AAAA和BBBB进入chunk B的元数据而且最终使用0x7ffdf020覆写chunk B的flink。从新编译而且装入调试器。如今当咱们分配chunk C(指向0x7ffdf020)时,咱们能够用shellcode填充这个chunk而且当触发异常时将会被调用。下面咱们能够看到以下代码设置PEB地址到EAX而且直接call调用0x20偏移量(FastPEBLockRoutine())把执行权转交到咱们的代码。

如今咱们对EIP拥有了直接的控制而且能够利用它返回到代码中。在这里不考虑绕过DEP以及执行代码。

程序特定指针攻击利用:

除非我提供一个在Windows XP SP3下使用程序特定指针利用该漏洞的例子,不然这篇文章不会完美结束。当目标针对一个包含了堆溢出的软件时,任何出如今溢出以后的硬编码可写可运行的函数调用都应该被针对并利用。

举例来讲,winsock的WSACleanup(),它在XP SP3下包含了一个位于0x71ab400a的硬编码函数调用。这也能够成为咱们用于往内存中写shellcode的地址。那样的话当WSACleanup()(或者许多其它winsock函数)执行时,它会重定向回shellcode。下面是对于WSACleanup()反汇编而且寻找硬编码函数调用:

几乎全部windows下面的网络程序都喜欢使用从winsock导出的函数调用而且特别是(原文是spefcially,想必是specially打错了)WSACleanup()用来清理任何socket链接,而且这些多数在溢出后确定会被调用。所以,使用这个函数指针(0x71ac4050)来覆写 看上去运行时始终一致(原文consistantly,想必是consistently打错了)。另外一个例子显示,recv()函数也包含了一个相同函数的调用。

若是咱们跟踪0x71ab678f函数调用,咱们能够看到咱们到了这里

你知道了什么?另外一个顺下来对0x71ac4050的调用,为了确保这个会成功,让咱们看看这段内存的访问权限。

这种技术的基本难题是 由于你将会覆写那个区域,因此任何使用了winsock的shellcode(几乎全部我用的shellcode)都会失效。一种解决这难题的办法是再次把0x71ac4050恢复到原来的代码这样winsock就能工做了

程序特定指针攻击利用例子:

我提供了一个有漏洞的服务器程序(多数代码来自在infosec institute的Stephen Bradshaws blog,全部的赞扬归他)而且如是调整使其包括了堆溢出以及内存泄露。

构造一个'PoC'利用的思路是 经过正确的方式触发而且列出堆栈内容这样你就能够覆写一个在lookaside的chunk而后达到代码执行。下载this文件而且在windows xp sp3下运行而后尝试我在这里提供的例子来帮助你抓住这个技术的知识要点。我已经决定不提供这个的源代码这样人们必须作一些你想工做来寻找正确堆栈布局而后达到命令执行。做为一个提示(但愿不是很透),这里有一张'PoC'经过肯定堆布局工做的截图

固然,任何状况下只要你能弄清堆内存的布局都很好。一个列出目标进程的堆的更简单的目标是稳妥地利用客户端。随着可以编写和控制堆的布局,你必定可以构造一个经过堆溢出攻击利用的场景。一个例子是利用Alex Soritov的heaplib.js来协助分配,释放以及在堆内存中实行其余字符串操做。若是使用MSHTML在相同堆中利用javascript或者DHTML来分配或者释放chunk,那么你就能够控制堆管理器而且你能够在浏览器中经过堆溢出转交执行控制。

对AOL 9.5 (CDDBControl.dll) ActiveX堆溢出的分析:

我决定看一下这个漏洞而且判断其在windows xp sp3下的可利用程度。尽管control被标记为脚本或初始化不安全,我以为漏洞分析起来仍是颇有意思的。我原本不想添加这一小节,可是sinn3r问到了,因此我决定加进去 :)如今因为它在ActiveX控制下,一种触发的方式为经过使用一种可选的脚本语言在IE浏览器触发。我决定使用JavaScript由于其可延展性而且heapLib.js被含在里面。我使用的环境以下所示:

1.  IE 6/7 
2.  XP SP3 
3.  heapLib.js 
复制代码

因此让欢乐开始吧!首先我触发了exploit-db上的Hellcode Research写的PoC。让咱们来分析程序的crash

咱们在这里能够看到当前堆的segment实际上用光了未分配内存而且没法为那个堆segment提供更多的内存。

而后咱们来看一下:

咱们用光了堆segment而且没有能力建立一个新的segment。因此咱们该怎么办?好吧咱们知道咱们必须触发一个unlink操做。为了那样作,咱们须要windows堆管理器复制全部的数据进入分配的buffer(覆写其它的chunk)可是不丢失当前的segment。那么当下一此分配或者free被触发,它会试着去unlink。我修改了poc去只用2240字节代替4000字节来触发溢出

#!cpp
var x = unescape("%41"); 
while (x.length<2240) x += x; 
x = x.substring(0,2240); 
target.BindToFile(x,1); 
复制代码

如今当咱们触发这一漏洞时,咱们实际上并无让浏览器崩溃。固然chunk已经溢出了,可是直到再有一个其余的unlink操做 它不会崩溃。可是当关闭浏览器时,垃圾搜集器启动而且分配全部已经free的chunk而且如是多个对RtlAllocateHeap()的调用 而后漏洞被触发。此次事情看上去更实际了。

#!bash
(384c.16e0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=02270000 ecx=02273f28 edx=02270178 esi=02273f20 edi=41414141
eip=7c9111de esp=0013e544 ebp=0013e764 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
ntdll!RtlAllocateHeap+0x567:
7c9111de 8b10            mov     edx,dword ptr [eax]  ds:0023:41414141=????????
复制代码

太棒了,因此咱们有了一个潜在的攻击利用条件。在这种状况下,flink就是EAX而后blink就是EDI.带XP sp 0-1和如下版本,咱们能够简单地实行一次简单的UEF函数覆写而且获取控制。可是咱们已经可以经过浏览器强大的脚本能力给堆"按摩(原文massage,但我总以为像是message,呵呵)"这样咱们将会试着在xp sp3下对付这个漏洞。在分析堆的布局时,我快速地注意到Active X control实际上在运行时建立了它本身的堆而且crash是在freelist insert时被触发的

当使用heaplib.js库时,我能够成功地操做堆,那就是说,默认的进程堆不是 active X controls的堆。在这一点上,我大体能够说在Windows XP SP3及以上版本看上去不能利用,固然这多是糟糕的误解可是就目前我能够说,若是对象的堆没法被操做,那么它就不能被利用。

Hooking 钩子:

一个很是有用的提示是 知道当调试具备堆溢出的程序时调的是分配与free的数量以及它们的大小。在一个进程/线程的生命周期里,许多分配与free被执行而且确实对它们都下断点很是耗时。一件对immunity debugger不错的事情是你可使用!hookheap插件来hook RtlAllocateHeap() 和 RtlFreeHeap()而且这样你就能够找到全部在特定操做中执行的 分配和free 的大小和数量

正如你所见,一个特别的分配露了出来,一次大量字节的分配看上去像是对目标漏洞服务器的请求。

0x07 结语


堆管理器对于去理解、去利用堆溢出很是复杂 由于每一个状况都有不一样因此须要许多小时的分析。理解目标程序的当前上下文环境以及其中的限制是发掘一个堆溢出可利用性的关键。被微软强制的mitigation保护 基础地防御了大多数的堆溢出,不过期不时地咱们看到特定程序条件被攻击者利用的状况发生

References:

  1. http://windbg.info/doc/1-common-cmds.html
  2. http://www.insomniasec.com/publications/Heaps_About_Heaps.ppt
  3. http://cybertech.net/~sh0ksh0k/projects/winheap/XPSP2 Heap Exploitation.ppt
  4. some small aspects from: http://illmatics.com/Understanding_the_LFH.pdf
  5. http://www.blackhat.com/presentations/win-usa-04/bh-win-04-litchfield/bh-win-04-litchfield.ppt
  6. http://www.insomniasec.com/publications/Exploiting_Freelist[0]_On_XPSP2.zip
  7. http://www.insomniasec.com/publications/DEPinDepth.ppt (heap segment information)
  8. Advanced windows Debugging (Mario Hewardt)
  9. www.ptsecurity.com/download/defeating-xpsp2-heap-protection.pdf
  10. http://grey-corner.blogspot.com/2010/12/introducing-vulnserver.html
  11. http://www.immunityinc.com/downloads/immunity_win32_exploitation.final2.ppt
  12. Understanding and bypassing Windows Heap Protection by Nicolas Waisman (2007)
  13. http://kkamagui.springnote.com/pages/1350732/attachments/579350
相关文章
相关标签/搜索