【问题标题】:Example of a buffer overflow leading to a security leak导致安全漏洞的缓冲区溢出示例
【发布时间】:2011-03-18 13:01:21
【问题描述】:

我阅读了许多关于不安全函数(如 strcpy、memcpy 等)的文章,这些函数可能会在处理外部数据(如文件内容或来自套接字的数据)时导致安全问题。这听起来可能很愚蠢,但我编写了一个易受攻击的程序,但我没有设法“破解”它。

我了解缓冲区溢出的问题。拿这个示例代码:

int main() {
   char buffer[1];
   int var = 0;

   scan("%s", &buffer);
   printf("var = 0x%x\n", var);
   return 0;
}

当我执行程序并输入“abcde”时,程序输出 0x65646362,即十六进制 + little-endian 的“edcb”。但是我读到您可以修改被压入堆栈的 eip 值,以使程序执行一些不需要的代码(例如,在调用 system() 函数之前)。

然而函数的汇编是这样开始的:

push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp

由于 %esp 的值在函数开始时是随机的,并且由于这个“和”,似乎没有可靠的方法将精确值写入推送的 eip 值。

此外,我读到可以执行您在缓冲区中编写的代码(这里的缓冲区只有 1 个字节长,但实际上它足以存储一些代码)但是您会给什么价值eip 为了这样做(考虑到缓冲区的位置是随机的)?

那么为什么开发人员如此担心安全问题(除了程序可能崩溃)?您是否有易受攻击程序的示例以及如何“破解”它以执行不需要的代码?我在 linux 上试过这个,Windows 不安全吗?

【问题讨论】:

    标签: c security overflow buffer


    【解决方案1】:

    阅读 Aleph One 的精彩文章:Smashing the Stack for Fun and Profit

    【讨论】:

    • 如果我错了,请纠正我,但是,尽管它是开创性的,但我不记得这篇文章涉及任何堆栈保护机制。
    • @torak:不,它没有。不过,我不认为这是 OP 的问题,而且这个问题似乎比处理操作系统/硬件保护更基本。这篇文章是一个很好的起点。
    • @Moron:好吧,鉴于 OP 特别提到 and $0xfffffff0, %esp 指令将偏移量呈现给 EIP 变量,看来他们掌握了基本的溢出机制。
    • @torak:主要问题似乎是攻击者不知道 esp 的事实。使用 0xfffffff0 执行 and 不会使 esp 随机化或使其更难(如果事实如此,因为我们现在可以在 shellcode 中对齐返回地址:-))。 and 似乎所做的只是对齐堆栈帧边界。所以这对我来说似乎仍然是一个基本问题,我相信这篇文章(尤其是示例)解决了这个问题。
    • 我刚读了这篇文章,谢谢。不知道 jmp-call 提示获取缓冲区的地址,但是文章在覆盖 EIP 的值时有点过时了。
    【解决方案2】:

    首先,不要低估在 EIP 中不可靠地放置值所带来的危害。如果一个漏洞利用 16 次有效,并且它所攻击的服务会自动重启,就像许多 Web 应用程序一样,那么在尝试访问时失败的攻击者总是可以尝试,再试一次。

    另外,在很多情况下,ESP 的值并没有你想象的那么随机。对于 32 位系统的初学者来说,它几乎总是四的倍数。这意味着and $0xfffffff0, %esp 指令提供的额外填充将是 0、4、8 或 12 个字节。这意味着可以将要写入返回 EIP 的值重复四次,以覆盖返回 EIP 地址的所有可能偏移量。

    实际上有更激进的stack protection / buffer overflow detection 机制。但是,即使是这些,也有一些方法和手段。

    此外,对于此类事情可能存在危险的示例,请考虑var 的值是否对您的逻辑很重要,如下面的玩具示例所示。

    int main() {
      char buffer[1];
      int var = 0;
    
      var = SecurityCheck();
    
      scan("%s", &buffer);
      if (var != 0)
        GrantAccess();
      else
        DenyAccess()
    }
    

    【讨论】:

    • 我还要提到 nop 雪橇。简而言之,你可以在你的 shellcode 之前放置很多 NOP,所以猜测你的 shellcode 的“开始”在哪里变得更容易了。
    • 与此示例密切相关的是通过提供太长的name 来破坏struct user_account {char name[16]; int group; int permissions;}; 等结构的技术,以便覆盖grouppermissions 成员。关键是能够在数组后面的字节中预测内存中的内容。
    【解决方案3】:

    此外,您不必使用指向字符串中某些内容的指针来覆盖 EIP。例如,您可以使用指向system() 的指针覆盖它,并使用指向程序映像中固定位置的/bin/sh 的指针覆盖下一个单词。

    编辑:注意system 使用PATH(实际上它通过shell 运行命令),所以"sh" 也一样好;因此,字符串末尾以“sh”结尾的任何英文单词都提供了您需要的参数。

    【讨论】:

      【解决方案4】:

      基于缓冲区溢出的实际漏洞利用的经典示例是 1988 年的 Morris Worm

      【讨论】:

      • 实际上,Morris 蠕虫利用了几个漏洞,其中一个是fingerd 中的缓冲区溢出。
      • @ninjalj:是的,这看起来很准确。
      【解决方案5】:

      正如其他答案中提到的,绝对可靠性并不总是攻击成功的必要条件。自动重启的应用程序就是一个例子。 suid 程序上的本地可利用缓冲区溢出将是另一种情况。还有一种 NOP sled 技术可以增加成功利用的机会,在您的 shellcode 之前放置大量 NOP,这样您就有更好的机会正确猜测您的 shellcode 的“开始”。

      还有更多技术可以提高攻击的可靠性。在 Windows 上,过去,许多漏洞利用位于程序某处(蹦床)的“jmp %esp”地址覆盖了返回地址。

      “不安全的编程示例”对 Linux 有一个很好的技巧。清理您的环境并将您的 shellcode 放入环境变量中。在过去,这会导致堆栈顶部附近的可预测地址。

      还有诸如 return-into-libc 和 return-oriented 编程等变体。

      甚至还有一篇关于 Phrack 的文章关于如何利用 1 字节堆栈溢出(意味着缓冲区仅被一个字节溢出)(顺便说一句,在绝大多数情况下,1 字节堆溢出也是可利用的,除非采取保护措施)。

      总而言之,并不是开发人员偏执,即使是最奇怪的情况也有很多方法可以利用,请记住:

      • 一个程序在做它应该做的事情时是高质量的。
      • 程序在做它应该做的事情时是安全的,仅此而已

      【讨论】:

        【解决方案6】:

        这是一个windows版本和教程:

        http://www.codeproject.com/KB/winsdk/CodeInject.aspx

        我一直被警告的一般情况是:

        printf( string );
        

        因为用户可以在其中提供"%n",它允许您将任何您想要的内容插入内存。您需要做的就是找到系统调用的内存偏移量,传递一些"%n" 和垃圾字符,然后将内存地址插入到返回向量通常所在的堆栈中。瞧——插入任何你喜欢的代码。

        【讨论】:

        • 这叫做格式字符串漏洞,不是缓冲区溢出:)
        • @ninjali:嗯,他们是亲表亲。这个溢出堆栈缓冲区。 :-)
        猜你喜欢
        • 2016-06-04
        • 1970-01-01
        • 2020-07-13
        • 1970-01-01
        • 1970-01-01
        • 2013-02-01
        • 1970-01-01
        • 2021-09-16
        • 1970-01-01
        相关资源
        最近更新 更多