22

我一直在尝试学习堆溢出攻击的基础知识。我最感兴趣的是使用损坏或修改块元数据作为攻击的基础,但我也愿意接受其他建议。我知道我的漏洞利用目标应该是用函数指针覆盖printf()函数challenge()指针,但我似乎无法弄清楚如何实现该写入。我有以下要利用的代码,它正在使用mallocfrom glibc 2.11.2

void challenge()
{
        puts("you win\n");
}

int main(int argc, char **argv)
{
        char *inputA, *inputB, *inputC;

        inputA = malloc(32);
        inputB = malloc(32);
        inputC = malloc(32);

        strcpy(inputA, argv[1]);
        strcpy(inputB, argv[2]);
        strcpy(inputC, argv[3]);

        free(inputC);
        free(inputB);
        free(inputA);

        printf("execute challenge to win\n");
}

显然,实现对已分配块的元数据的实际覆盖是微不足道的。但是,我还没有找到使用任何标准技术来利用此代码的方法。我已阅读并尝试实施以下技术:

  • 论文:关于堆溢出的 w00w00
    • 虽然论文很清楚,但该unlink技术已经过时了一段时间。
  • Malloc Maleficarum.txt
    • 本文扩展了 w00w00 日以来的漏洞利用技术,并说明了 glibc 的较新版本。但是,鉴于本文详述的 5 种技术,我没有发现上面的代码符合这些技术的任何先决条件。
  • 通过打破堆来理解堆(pdf)
    • pdf 很好地回顾了堆的工作原理,但侧重于双重释放技术。

我最初试图通过操纵 inputC 块的大小值来利用此代码,以便它指向 inputC 块的头部。当这不起作用时,我尝试进一步指向 inputB 块。那时我意识到新的 glibc 会对大小值进行健全性检查。

假设用户能够将分配的块的元数据编辑为任意值,并使用它来覆盖 GOT 中的值或写入任何其他任意地址,那么用户如何利用 free 进行攻击?

注意:当我写“任意地址”时,我知道内存页可能是只读的或受保护的,我的意思是我可以假设我可以写入的地址。

4

3 回答 3

11

注意:在回答之前我会说,这纯粹是一个学术回答,不打算用于恶意目的。我知道 OP 正在做的练习,它们是开源的,并不打算鼓励任何用户在未经批准的情况下使用这些技术。

我将在下面详细介绍该技术,但为了您的参考,我将看看 Vudo malloc 技巧(在您上面的一个链接中引用了它),因为我的概述将是一个简短的概述:http ://www.phrack.com /issues.html?issue=57&id=8

它详细说明了 malloc 如何处理创建内存块、从列表中提取内存和其他事情。特别是 unlink 攻击对这种攻击很感兴趣(注意:您是正确的,glibc 现在出于这个特殊原因对大小执行完整性检查,但您应该使用较旧的 libc 进行此练习......遗留兄弟)。

从论文中可以看出,分配块和空闲块使用相同的数据结构,但数据处理方式不同。看这里:

chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         | prev_size: size of the previous chunk, in bytes (used   |
         | by dlmalloc only if this previous chunk is free)        |
         +---------------------------------------------------------+
         | size: size of the chunk (the number of bytes between    |
         | "chunk" and "nextchunk") and 2 bits status information  |
  mem -> +---------------------------------------------------------+
         | fd: not used by dlmalloc because "chunk" is allocated   |
         | (user data therefore starts here)                       |
         + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
         | bk: not used by dlmalloc because "chunk" is allocated   |
         | (there may be user data here)                           |
         + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
         |                                                         |
         |                                                         |
         | user data (may be 0 bytes long)                         |
         |                                                         |
         |                                                         |
 next -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         | prev_size: not used by dlmalloc because "chunk" is      |
         | allocated (may hold user data, to decrease wastage)     |
         +---------------------------------------------------------+

分配的块不使用 fd 或 bk 指针,但空闲的会。这在以后会很重要。您应该了解足够的编程知识,才能理解 Doug Lea 的 malloc 中的“块”被组织成一个双向链表;有一个用于空闲块的列表,另一个用于分配的块(从技术上讲,有几个空闲列表取决于大小,但在这里无关紧要,因为代码分配了相同大小的块)。所以当你释放一个特定的块时,你必须修复指针以保持列表的完整性。

例如,假设您正在从下面的列表中释放块 y:

x <-> y <-> z

请注意,在上图中,bk 和 fd 的位置包含沿列表迭代所需的指针。当 malloc 想要从列表中取出一个块 p 时,它会调用一个宏来修复列表:

#define unlink( y, BK, FD ) {            
    BK = P->bk;                          
    FD = P->fd;                          
    FD->bk = BK;                         
    BK->fd = FD;                         
}

宏本身并不难理解,但在旧版本的 libc 中需要注意的重要一点是,它不对大小或正在写入的指针执行完整性检查。在您的情况下,这意味着没有任何类型的地址随机化,您可以预测和可靠地确定堆的状态,并通过以特定方式溢出堆(通过此处的 strncopy)将任意指针重定向到您选择的地址.

要使攻击起作用,需要做一些事情:

  • 您的块的 fd 指针指向您要覆盖的地址减去 12 个字节。偏移量与 malloc 在修改列表时清理对齐有关
  • 你的块的 bk 指针指向你的 shellcode
  • 大小需要为-4。这完成了一些事情,即它设置块中的状态位

因此,您必须在特定示例中使用偏移量,但您尝试使用 strcpy 传递的一般恶意格式是以下格式:

| 垃圾填满合法缓冲区| -4 | -4 | 您要覆盖的地址 -12 (0x0C) | 您想调用的地址

请注意,负数将 prev_size 字段设置为 -4,这使得自由路由认为 prev_size 块实际上开始于您控制/正在破坏的当前块。

是的,如果不提到这种攻击不适用于当前版本的 glibc,那么正确的解释是不完整的。大小已完成完整性检查,而 unlink 方法将不起作用。这与地址随机化等缓解措施相结合,使得这种攻击在遗留系统之外的任何东西上都不可行。但是这里描述的方法是我如何应对挑战的;)

于 2013-03-20T15:20:49.610 回答
3

请注意,Malloc Malleficarum 中解释的大多数技术现在都受到保护。glibc 已经改进了很多双重免费场景。

如果您想提高对 Malloc Malleficarum 技术的了解,请阅读 Malloc Des-Malleficarum 和 Lore 之家:由 blackngel 编写的重载。您可以在 phrack 中找到这些文本。

Malloc Des-Malleficarum

我也在努力,我可以告诉你,例如,心灵之家不再可利用,至少,正如文本中所解释的那样。尽管有可能绕过添加到代码中的新限制。添加执行代码的最简单方法是覆盖 .dtors 地址,因此您的代码将始终在程序完成后执行。

如果您下载 glibc 代码并研究 malloc 的批评区等,您会发现前面提到的文档中没有记录的代码检查。包括这些检查以阻止双重免费派对。

另一方面,您可以在 youtube (BlackHat 2007) 中找到 Justin N. Ferguson 的介绍(通过打破堆来理解堆)对于理解所有堆机制来说是完美的,但我必须承认,所展示的技术是远非可靠,但至少,他为堆利用开辟了一个新领域。

通过破坏堆来理解它

无论如何,我也在努力,所以如果你想联系我,我们可以分享我们的进步。您可以在 overflowedminds.net 域中以 newlog 的身份与我联系(自己构建邮件地址 ^^)。

于 2012-04-12T13:01:41.490 回答
-2

堆溢出很难实现,并且非常依赖于堆布局,尽管看起来您正在使用 Windows CRT 堆,它有很多专门用于阻止此类攻击的缓解措施。

如果你真的想做这种事情,你需要高兴地跳进 WinDbg 并进入像 free 这样的函数,看看 free 内部到底发生了什么,因此你可以通过堆实现什么样的控制上一个值的溢出。

我不会给你任何比这更具体的帮助,原因很简单,证明堆溢出对于防御性安全来说通常就足够了——防御性安全专家可以报告堆溢出,而无需真正充分利用它。唯一需要完全利用堆溢出一直到远程代码执行的人是攻击性地利用错误的人如果你想这样做,你就靠你自己了。

于 2012-03-11T20:10:24.797 回答