跳进argv?

时间:2010-05-01 13:40:19

标签: c linux assembly gdb buffer-overflow

我正在试验shellcode并偶然发现了nop-slide技术。我写了一个小工具,它将buffer-size作为参数并构造一个这样的缓冲区:[NOP | SC | RET],NOP占用缓冲区的一半,然后是shellcode,其余的填充了(猜到的)返回地址。它与他的着名论文中描述的工具aleph1非常相似。

我易受攻击的测试应用程序与他的论文相同:

int main(int argc, char **argv) {
char little_array[512];
if(argc>1)
    strcpy(little_array,argv[1]);   
return 0;
}

我测试了它很好,它有效:

jth@insecure:~/no_nx_no_aslr$ ./victim $(./exploit 604 0)
$ exit

但说实话,我不知道为什么。好吧,保存的eip被按预期覆盖了,但我认为它没有跳到某个缓冲区,而是跳进了argv。

gdb在调用 strcpy()之前显示以下地址

(gdb) i f  
Stack level 0, frame at 0xbffff1f0:  
 eip = 0x80483ed in main (victim.c:7); saved eip 0x154b56  
 source language c.  
 Arglist at 0xbffff1e8, args: argc=2, argv=0xbffff294  
 Locals at 0xbffff1e8, Previous frame's sp is 0xbffff1f0  
 Saved registers:  
  ebp at 0xbffff1e8, eip at 0xbffff1ec  

little_array的地址:

(gdb) print &little_array[0]
 $1 = 0xbfffefe8 "\020"

strcpy()之后:

(gdb) i f
Stack level 0, frame at 0xbffff1f0:
 eip = 0x804840d in main (victim.c:10); saved eip 0xbffff458
 source language c.
 Arglist at 0xbffff1e8, args: argc=-1073744808, argv=0xbffff458
 Locals at 0xbffff1e8, Previous frame's sp is 0xbffff1f0
 Saved registers:
  ebp at 0xbffff1e8, eip at 0xbffff1ec

那么,这里发生了什么?我用一个604字节的缓冲区来溢出little_array,所以他肯定覆盖了保存的ebp,保存了eip和argc以及带有猜测地址0xbffff458的argv。

然后,在返回后,EIP指向0xbffff458。但是little_buffer驻留在0xbfffefe8,这是1136字节的差异,所以他肯定没有执行little_array。我按照 stepi 命令执行,然后在0xbffff458及之后,他执行NOP并到达shellcode。

我不太清楚为什么会这样。首先,我是否正确他在argv中执行我的shellcode,而不是little_array?加载器(?)在哪里将argv放到堆栈上?我认为它紧跟在argc之后,但是在argc和0xbffff458之间,有一个620字节的间隙。他怎么可能成功地“登陆”地址为0xbffff458的NOP-Pad,这是在0xbffff1ec上保存的eip之上?

有人可以澄清一下吗?我实际上不知道为什么这样做。我的测试机器是没有ASLR的Ubuntu 9.10 32位机器。受害者有一个可执行堆栈,使用execstack -s设置。

提前致谢。

1 个答案:

答案 0 :(得分:3)

首先,绘制堆栈图。

0xBFFFF4F9 |          |
            ----------
          ...   
0xBFFFF29E |    NOP   |
0xBFFFF29D |    NOP   | argv[1]   * guestimate
            ----------
0xBFFFF29C |   '\0'   |
          ...
0xBFFFF295 |    '/'   |
0xBFFFF294 |    '.'   | argv[0] "./victim"
            ----------
          ... 
            ----------
0xBFFFF1F8 |   NULL   |
0xBFFFF1F8 | &argv[1] |
0xBFFFF1F4 | &argv[0] | argv
            ---------- 
0xBFFFF1F0 | 0x000002 | argc
            ---------- 
          ...
0xBFFFEFE9 |          |
0xBFFFEFE8 |          | little_array
            ----------

(注意:在图中,有些框每行保留1个字节,其他4或8个,具体取决于存储在其中的类型)

  

加载器(?)在哪里将argv放到堆栈上?

调用argc后,argvstrcpy会被覆盖,表明他们可能高于little_array。我把它们放在0xBFFFF1F0,因为那是前一帧堆栈结束的地方,main的帧似乎没有空间,即使GDB说arglist位于0xBFFFF1E8。它在我的系统上撒谎 - argcargv低于little_array。请尝试p &argvp &argc查看确定的位置。

由于0xBFFFF458在argv [1]内,argv [1]中的shellcode确实会被执行,而不是little_array中的shellcode。由于shell代码有两个副本(一个在little_array中,另一个在argv[1]中),因此可以执行,具体取决于您猜测的返回地址。

  

我认为[argv]紧跟在argc之后,但是在argc和0xbffff458之间,有一个620字节的间隙。

0xBFFFF458是漏洞利用后存储在 argv中的值(请注意argc(signed)0xBFFFF458 == -1073744808保持一致)。之前,argv拥有0xbffff294。在这两种情况下,argv本身都存储在其他地方。