格式化字符串漏洞 - 用于读取随机地址的堆栈组织

时间:2013-10-17 15:37:07

标签: string security assembly format stack

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

我可以实时查看在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
(...)

0x7ffffff4848 “%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”读取?

从我上面看到的情况来看,只有整个字符串的地址被放在堆栈上( 0x00007fffffffe48d ),而不是字符(实际上是我打算阅读的地址)本身。 / p>

换句话说,不管我放在我的字符串里面,我只能控制地址的内容:

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

但不会从堆栈中弹出什么。

1 个答案:

答案 0 :(得分:0)

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

很遗憾,您不会显示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。