【问题标题】:Format string vulnerability - stack organization for reading a random address格式字符串漏洞——读取随机地址的堆栈组织
【发布时间】:2013-10-26 04:52:56
【问题描述】:

我试图了解格式字符串漏洞是如何工作的,以及我们如何读取任何地址。

我可以实时检查在 printf 中输入“%x %x %x”如何将元素从堆栈中弹出字符串地址上方。

这就是堆栈在 printf 中的外观:

(...)
0x7fffffffe018:    0x000000000040052c
0x7fffffffe020:    0x00007fffffffe180    
0x7fffffffe028:    0x00007fffffffe168
0x7fffffffe030:    *0x00007fffffffe48d* << address of argument   
0x7fffffffe038:    0x00007ffff7dd4e80 
0x7fffffffe040:    0x00007ffff7de9d60    
0x7fffffffe048:    0x00007ffff7ffe268
0x7fffffffe050:    0x0000000000000000    
0x7fffffffe058:    0x0000000000400563 <</ return address after printf
0x7fffffffe060:    0x00007fffffffe168    
0x7fffffffe068:    0x0000000200400440
(...)

0x7ffffffffe48d"%x %x %x" 字符串在内存中的地址:

0x7fffffffe469:    "/home/.../C/format_string/test"
*0x7fffffffe48d*:    "%x %x %x"
0x7fffffffe4ac:    "SSH_AGENT_PID=..."

因此从逻辑上讲,这将在此之上输出 3 个元素,即:

ffffe168 ffffe180 40052c 

现在,我不明白的是,如果我在参数中放一个随机地址,说: "\x15\xff\x0A\x23 %x %x %x %x %s",为什么"\x15\xff\x0A\x23"存储在堆栈中并由“%s”读取?

根据我在上面看到的,只有整个字符串的地址被放入堆栈(0x00007ffffffffe48d),而不是字符(实际上是我打算读取的地址)本身。

换个说法,无论我在字符串中放什么,我只能控制地址的内容:

0x7fffffffe48d:    "blablabla %x %x %x"

但不是从堆栈中弹出的内容。

【问题讨论】:

    标签: string security assembly format stack


    【解决方案1】:

    你是对的,代码不会将字符串的内容压入堆栈。
    它只是推送它的地址。
    在这里使用缓冲区溢出技术将不允许您覆盖返回地址。

    很遗憾,您没有显示 C 代码。

    以下 C 代码会将其字符串数据放入堆栈,因此可能会被利用。
    以下示例来自:http://insecure.org/stf/smashstack.html

    基于堆栈的字符串 - 可利用

    void overrunmybuffer(char *str) {
       char buffer[16];           <<-- local fixed sized array, stored on the stack.
    
       //should have used `strncpy()`    
       strcpy(buffer,str);  <<-- strcpy will blindly push 256 bytes in a 16 byte buffer
    }
    
    void main() {
      char large_string[256];
      int i;
    
      for( i = 0; i < 255; i++)
        large_string[i] = 'A';
    
      overrunmybuffer(large_string);
    }
    

    基于堆的字符串 - 难以利用
    在您的代码中,函数看起来像这样:

    void cannotoverrunstack(char *str) {
       char *buffer;       <<-- pointer to char, only the address is stored on stack
       buffer = malloc(16);  
       strcpy(buffer,str); <<-- some other data in the heap will be overwritten
                                but not the stack.
    } 
    

    请注意,堆上的数据覆盖可能会也可能不会触发访问冲突,并且可能会覆盖有用的数据。
    由于在堆中分配数据的方式是随机的,因此与覆盖堆栈相比,它的用处要小得多。
    堆栈帧具有非常可预测的布局,因此非常易于操作。

    缓冲区溢出问题的正确解决方案是永远不要使用strcpy,而只使用strncpy,如下所示:

    为固定大小的缓冲区保存代码

    void cannotoverrunmybuffer(char *str) {
       char buffer[16];           <<-- local fixed sized array, stored on the stack.
    
       strncpy(buffer,str,sizeof(buffer)-1);  <<-- only copy first 15 bytes. 
       buffer[15] = 0;                       <<-- !!put terminating 0 in!
    }
    

    不要忘记强制零终止您的字符串,否则您最终会读到超过字符串的末尾,从而导致丢失。如果您使用的是 Unicode 字符串,则需要输入两个终止的 0。

    更好的是使用自动扩展的字符串,如 Java 或 Delphi 中使用的那些(C++ 中的System::OpenString)。 缓冲区溢出是不可能的,因为它们会自动从 0 扩展到 2GB。

    【讨论】:

      猜你喜欢
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-07
      • 1970-01-01
      • 1970-01-01
      • 2014-02-05
      • 2014-11-22
      相关资源
      最近更新 更多