0

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

我可以实时检查在 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"

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

4

1 回答 1

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(System::OpenString在 C++ 中)中使用的那些。缓冲区溢出是不可能的,因为它们会自动从 0 扩展到 2GB。

于 2013-10-18T12:16:35.590 回答