联系我们联系我们
电子邮箱电子邮箱

揭开ARM“归零保护”的神秘面纱

[复制链接]
  • TA的每日心情
    无聊
    2019-9-27 09:24
  • 签到天数: 2 天

    [LV.1]初来乍到

    jia91921314 发表于 2019-10-9 17:43:09
    870 0
    本帖最后由 jia91921314 于 2019-10-9 17:48 编辑

    安全社区非常熟悉已经存在了几十年的x86体系结构。相比之下,ARM相对较新。在今天这个时代,随着我们转向更小、更便携的设备,在这类设备上使用ARM处理器的情况越来越多。ARM是一个自然的选择,因为与主流的x86处理器相比,它对功耗的要求更低。然而,节能并不意味着它在任何方面的能力下降。我们确实有高端ARM处理器,它不仅对电池性能温和,而且能够提供额外的能量来维持计算密集型的工作负载。这是由几个架构的选择,这超出了这篇文章的范围。
    因此,从攻击者和防御者的角度了解基于ARM的设备的利用是有意义的。熟悉x86之后,我们自然希望将现有的知识转换为ARM。但是,许多x86开发技术并不与ARM一一对应。为了绕过No Execute (NX),通常在x86上使用returnto - libc (ret2libc)技术。这对ARM不起作用。在这篇文章中,我们将探讨ret2libc以及它在ARM上失败的原因。我们还将考虑将returnto - zero保护(ret2zp)作为替代,它克服了适用于ARM系统的ret2libc的所有限制。

    返回libc:一个引物

    为了简单起见,我们将在练习中关闭地址空间布局随机化(ASLR)。这可以通过跑步来实现



    现在考虑下面的代码。



    do_echo函数中的缓冲区溢出错误很明显。get函数用于将输入输入到100字节的固定大小缓冲区。如果我们提供超过100个字节,就会溢出缓冲区,可能会覆盖堆栈上的其他值。在x86上,返回地址也存储在堆栈中。因此,通过使用适当的值覆盖返回地址,可以将执行重定向到我们选择的任意点。假设我们要炸一个壳。在这种情况下,我们可以将执行重定向到libc中的系统功能。系统函数接受一个参数—一个指向空终止ASCII字符串的指针,该字符串表示要执行的命令。

    在x86上,调用约定要求函数参数必须在堆栈上传递。在我们的例子中,当我们点击system时,它会期望指向命令字符串的指针也在堆栈上。缓冲区溢位错误已经让我们可以控制堆叠。因此,需要创建一个合适的字符串来满足所有约束。

    代码使用以下标志进行编译。我们正在以x86为目标进行编译,这就是我们指定-m32标志的原因。为简单起见,-fno- Stack -保护者禁用了Stack Smashing保护。



    首先,我们需要找出输入字符串的哪一部分覆盖了保存的返回地址。我们可以使用pwntools来生成一个大字符串



    接下来,调试gdb中的二进制文件(gdb -q ./echo-x86)。do_echo的分解是这样的。



    我们在do_echo的最后一条指令0x80484c1上设置了一个断点。运行程序并提供pwntools生成的字符串作为输入。



    我们立即到达断点。



    我们在ret指令上暂停了。此指令从堆栈中弹出一个dword并将执行传输到该地址。寄存器esp指向堆栈的顶部——0xbffffff18c。堆栈上最上面的dword是“daab”(在ascii中)。[注:“daabeaab”是一个qword,我们只需要最上面的dword]。ret指令将把这个作为返回地址,并尝试跳转它,这显然会使程序崩溃。现在,我们要找出输入中的“daab”的偏移量。同样,我们使用pwntools。



    “daab”的偏移量为112。这意味着我们在这个偏移上的任何东西都将被视为返回地址。我们想跳转到系统,所以让我们找到它的地址。



    /bin/sh位于0xb7f649ab。这就是我们需要的全部信息。我们的攻击字符串看起来是这样的



    前112个字节用于填充缓冲区。然后我们输入系统地址,最后是字符串“/bin/sh”的地址。在这之间,我们放入另一个值——假的返回地址。它的目的是什么?之所以设置这个值是因为函数调用在x86上的工作方式。x86上的调用指令实现函数调用。这条指令跳转到指定的地址,同时将返回地址压入堆栈。因此,当我们在系统中时,堆栈上最上面的dword应该是返回地址,然后是函数参数。我们的案例的返回地址并不重要,因此我们可以在这里放置任何值。

    为了生成攻击字符串,我们将使用Python脚本。



    为了模拟在远程服务器上运行的程序,我们将使用netcat。Python脚本将连接到指定的端口并发送攻击字符串。正如我们下面看到的,利用工作,我们得到一个外壳!



    这种开采方法被称为返利法。之所以这么叫,是因为我们将执行重定向到libc中的一个函数system。在这篇文章的开头,我们提到了ret2libc在ARM上是不可能的,也就是说,如果我们想利用完全相同的程序,但在ARM上编译,那么这种技术是行不通的。

    为什么在ARM上不能返回libc ?

    ARM不同于x86。特别是,在ARM上,函数的前四个参数分别在寄存器R0、R1、R2和R3中传递。剩下的参数(如果有的话)在类似于x86的堆栈上传递。在ret2libc中,我们将执行重定向到只接受单个参数的系统函数。ARM的调用约定要求这个参数必须在R0中通过。利用缓冲区溢出,我们只能控制堆栈,而不能控制寄存器。因此,一方面可以将执行重定向到系统,另一方面我们不能在R0中设置值。这使得ret2libc对ARM无效。

    引入归零保护

    虽然我们不能控制寄存器,这是一个成功的ret2libc攻击所必需的,但我们可以控制返回地址。在ARM上,返回地址存储在链接寄存器(LR)中。LR中的值由函数序言保存在堆栈中,因此在函数结束执行时在函数尾声中恢复它。为了明确这一点,让我们在一个Raspbian虚拟机中为ARM编译相同的代码。GEF的作者Hugsy已经为包括ARM在内的多个架构提供了预配置的vm。您可以从谷歌驱动器链接下载armv6-stretch VM以进行练习。

    在Raspbian虚拟机上,echo.c文件是用以下标记编译的。



    像以前一样禁用ASLR。在GDB (GDB -multiarch -q ./ echoc -arm)上运行二进制文件,do_echo的汇编代码如下



    第一个104c8指令将该值推入堆栈上的链接寄存器(LR)。LR寄存器存储返回地址。函数快结束时,在104f4,保存的返回地址被恢复到程序计数器,其效果与跳转回调用者相同。与ret2libc类似,我们可以溢出堆栈缓冲区并覆盖保存的返回地址。然而,这并不能让我们控制寄存器。

    在这种情况下,我们不是将执行重定向到系统,而是跳转到另一个位置,即一系列将值从堆栈加载到寄存器的指令(gadget)。因此,我们以一种间接的方式获得对寄存器的控制。
    如果我们查看lrand48的反汇编,它是一个libc函数,来自0xb6ea2fd4的一系列指令从堆栈加载寄存器R0,在弹出一个dword并跳转到它之前将堆栈指针增加12。这正是我们所需要的。ldr r0, [sp, #4]指令让我们控制寄存器r0,而pop {pc}允许我们在执行这个小工具后将执行重定向到系统。



    因此,我们需要以这样一种方式来编写利用字符串,以便首先跳转到这个小工具,它将把R0设置为字符串“/bin/sh”的地址。在调试二进制文件时,可以在gdb中找到所需的地址。



    在Raspbian虚拟机上使用gdbserver调试程序,并使用gdb-multiarch从主机连接到它。现在,让我们在do_echo的最后一条指令0x104f4处设置一个断点。这是为了查找将覆盖返回地址的输入部分的偏移量。



    在Raspbian虚拟机中提供一个pwntools生成的字符串作为输入。



    断点将被命中,如下所示。



    现在让我们检查堆栈。pop {r11, pc}指令将把“zaab”和“baab”分别装入寄存器r11和pc。“baab”覆盖保存了返回地址。使用pwntools,我们可以像以前一样找到偏移量。



    偏移量为104的dword将覆盖返回地址。我们现在可以编写Python脚本来生成攻击字符串



    前104个字节用于填充缓冲区。然后我们把小器具的地址写在lrand48。这是执行的最初方向。这个小工具将在[sp 4]加载带有dword的R0,因此在输入地址“/bin/sh”之前,我们需要放一个4字节的占位符来进行调整。接下来,堆栈指针增加12。为了弥补这一点,我们需要在最后放入系统地址之前放入另一个4字节的填充。

    与前面一样,我们使用netcat模拟远程服务器并运行二进制文件。回到主机上,开始运行,我们得到了一个外壳。



    这就是归零保护技术的工作原理。简而言之,它类似于ret2libc,但它是两个步骤的过程,在最终跳转到系统之前,我们首先将执行重定向到一个小工具,以便控制寄存器。
    以上就是这篇博客的全部内容。最后,我想补充一点,我们最近推出了一个基于视频的ARM开发培训课程,您将能够学习和理解类似的技术。如果你感兴趣,我强烈建议你尝试一下。如果你有任何问题或疑问,请在下方留言。

    原文链接:https://blog.attify.com/demystifying-ret2zp/
    华梦君评论:在今天这个时代,随着我们转向更小、更便携的设备,在这类设备上使用ARM处理器的情况越来越多。ARM是一个自然的选择,因为与主流的x86处理器相比,它对功耗的要求更低

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    发表新帖