【问题标题】:Buffer overflow in CC中的缓冲区溢出
【发布时间】:2011-09-07 09:24:49
【问题描述】:

我正在尝试在 Mac OS X 10.6 64 位上使用 C 编写一个简单的缓冲区溢出。这是概念:

void function() {
    char buffer[64];
    buffer[offset] += 7;    // i'm not sure how large offset needs to be, or if
                            // 7 is correct.
}

int main() {

    int x = 0;
    function();
    x += 1;
    printf("%d\n", x);      // the idea is to modify the return address so that
                            // the x += 1 expression is not executed and 0 gets
                            // printed

    return 0;
}

这是 main 的汇编程序转储的一部分:

...
0x0000000100000ebe <main+30>:   callq  0x100000e30 <function>
0x0000000100000ec3 <main+35>:   movl   $0x1,-0x8(%rbp)
0x0000000100000eca <main+42>:   mov    -0x8(%rbp),%esi
0x0000000100000ecd <main+45>:   xor    %al,%al
0x0000000100000ecf <main+47>:   lea    0x56(%rip),%rdi        # 0x100000f2c
0x0000000100000ed6 <main+54>:   callq  0x100000ef4 <dyld_stub_printf>
...

我想跳过 movl 指令,这意味着我需要将返回地址增加 42 - 35 = 7(对吗?)。现在我需要知道返回地址的存储位置,以便计算正确的偏移量。

我尝试手动搜索正确的值,但要么打印出 1,要么我得到 abort trap - 是否有某种缓冲区溢出保护正在进行?


在我的机器上使用 88 的偏移量。我使用了 Nemo 的方法来查找返回地址。

【问题讨论】:

  • 如果你的堆栈没有被清理并恢复保存的寄存器,你可能会在主函数的寄存器中得到垃圾。谁对此负责取决于编译器使用的函数调用约定。 en.wikipedia.org/wiki/X86_calling_conventions
  • 你应该把它标记为作业还是什么?您可能不希望人们认为您这样做是为了学习以外的目的。
  • @filipe:这样做了,我最初没有这样做,因为这真的很基础(你在大学第一年就学会了)。

标签: c buffer-overflow


【解决方案1】:

您可以尝试在调试器中运行您的代码,一次执行每条装配线,并检查堆栈的内存空间以及寄存器。

【讨论】:

    【解决方案2】:

    拆开function(),看看它长什么样。

    偏移量需要为 negative 正数,可能是 64+8,因为它是一个 64 位地址。此外,您应该在指针大小的对象上执行“+7”,而不是在字符上。否则,如果这两个地址跨越 256 字节边界,您将利用您的漏洞利用....

    【讨论】:

    • 不,不是负面的。由于堆栈不断增长,一旦超过缓冲区的高端,他就会开始破坏堆栈数据。
    • 我认为偏移量不需要为负数...在 x86 上,堆栈会向下增长。
    【解决方案3】:

    Roddy 是对的,您需要对指针大小的值进行操作。

    我将从读取您的漏洞利用函数中的值开始(并打印它们)而不是写入它们。当您爬过数组的末尾时,您应该开始看到堆栈中的值。不久之后,您应该会找到返回地址,并能够将其与您的反汇编程序转储对齐。

    【讨论】:

    • 不错的方法,我刚刚找到了偏移量(在我的例子中,它是 88)。
    【解决方案4】:

    我总是喜欢对漂亮的数据类型进行操作,比如这个:

    struct stackframe {
        char *sf_bp;
        char *sf_return_address;
    };
    
    void function() {
        /* the following code is dirty. */
        char *dummy;
        dummy = (char *)&dummy;
        struct stackframe *stackframe = dummy + 24; /* try multiples of 4 here. */
    
        /* here starts the beautiful code. */    
        stackframe->sf_return_address += 7;
    }
    

    使用此代码,您可以通过调试器轻松检查stackframe-&gt;sf_return_address 中的值是否符合您的预期。

    【讨论】:

      【解决方案5】:

      这个 32 位 示例说明了如何计算它,请参阅下面的 64 位:

      #include <stdio.h>
      
      void function() {
          char buffer[64];
          char *p;
          asm("lea 4(%%ebp),%0" : "=r" (p));  // loads address of return address
          printf("%d\n", p - buffer);         // computes offset
          buffer[p - buffer] += 9;            // 9 from disassembling main
      }
      
      int main() {
          volatile int x = 7;
          function();
          x++;
          printf("x = %d\n", x); // prints 7, not 8
      }
      

      在我的系统上,偏移量是 76。这是缓冲区的 64 个字节(请记住,堆栈会向下增长,因此缓冲区的开头远离返回地址)加上介于两者之间的任何其他碎屑。

      显然,如果您正在攻击现有程序,则不能指望它为您计算答案,但我认为这说明了原理。

      (另外,我们很幸运+9 没有执行到另一个字节中。否则单字节增量不会按照我们的预期设置返回地址。如果你在@ 内不走运返回地址,这个例子可能会中断987654326@)

      我不知何故忽略了原始问题的 64 位。 x86-64 的等价物是 8(%rbp),因为指针有 8 个字节长。在这种情况下,我的测试构建恰好产生了 104 的偏移量。在上面的代码中,替换 8(%%rbp) 使用双 %% 在输出程序集中获得单个 %。这在this ABI document 中有描述。搜索8(%rbp)

      cmets 中有一个抱怨,4(%ebp)76 或任何其他任意数字一样神奇。事实上,寄存器%ebp(也称为“帧指针”)的含义及其与堆栈上返回地址位置的关系是标准化的。我很快用谷歌搜索了一个插图是here。那篇文章使用术语“基本指针”。如果您想在其他架构上利用缓冲区溢出,则需要同样详细了解该 CPU 的调用约定。

      【讨论】:

      • 这又增加了一个神奇的数字 :-) 解释为什么它是 4(%%ebp) 而不是 42(%%ebp) 会很有用!
      • @Ben。我很惊讶它是 4,因为我认为 64 位操作系统需要 8...?
      • x86_64 GCC 默认省略帧指针(并且只使用堆栈指针的偏移量),所以这不起作用。
      • 当然不是 默认情况下,因为我使用了 GCC 并且效果很好。但是,如果您打开优化,它会。我的回答是试图引导提问者获得更大的理解,这将有助于他回答自己的问题。然而,他接受了另一个答案,并评论说他尝试了一些随机值,直到某些东西起作用。
      • @ryyst:当你对一个函数构造溢出攻击时,它将特定于一个精确的编译版本。对 my function() 起作用的值可能与在您的问题中对 function() 起作用的值不同。如果您将char *p 移动到我的char buffer 上方,它可能会再次更改。看看这个激活记录的解释:en.wikipedia.org/wiki/Call_stack
      猜你喜欢
      • 2012-02-05
      • 1970-01-01
      • 1970-01-01
      • 2015-12-26
      • 1970-01-01
      • 2015-12-16
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      相关资源
      最近更新 更多