3

我正在尝试使用 shellcode 并偶然发现了 nop-slide 技术。我写了一个小工具,它以缓冲区大小为参数并构造一个像这样的缓冲区:[ 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,所以他肯定用猜测的地址 0xbffff458 覆盖了保存的 ebp、保存的 eip 和 argc 以及 argv。

然后,返回后,EIP 指向 0xbffff458。但是 little_buffer 位于 0xbffffefe8,相差 1136 字节,所以他肯定没有执行 little_array。我使用stepi命令执行,然后,在 0xbffff458 及以后,他执行 NOP 并到达 shellcode。

我不太确定为什么会这样。首先,我是否正确,他在 argv 中执行我的 shellcode,而不是 little_array?加载器(?)将 argv 放在堆栈的什么位置?我以为它紧跟在 argc 之后,但是在 argc 和 0xbffff458 之间,有 620 个字节的间隙。他怎么可能成功地“降落”在地址 0xbffff458 的 NOP-Pad 中,这比保存的 eip 0xbffff1ec 高得多?

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

提前致谢。

4

1 回答 1

3

首先,绘制堆栈图。

0xBFFFF4F9 | |
            ----------
          ...   
0xBFFFF29E | 诺普 |
0xBFFFF29D | 诺普 | argv[1] * 猜测
            ----------
0xBFFFF29C | '\0' |
          ...
0xBFFFF295 | '/' |
0xBFFFF294 | '。' | argv[0] "./受害者"
            ----------
          ...
            ----------
0xBFFFF1F8 | 空 |
0xBFFFF1F8 | &argv[1] |
0xBFFFF1F4 | &argv[0] | argv
            ----------
0xBFFFF1F0 | 0x000002 | 氩气
            ----------
          ...
0xBFFFEFE9 | |
0xBFFFEFE8 | | 小数组
            ----------

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

加载器(?)将 argv 放在堆栈的什么位置?

argcargv在调用后被覆盖,strcpy表明它们可能高于little_array. 我将它们放置在 0xBFFFF1F0,因为这是上一帧的堆栈结束的地方,并且在 的帧中似乎没有空间main,即使 GDB 说 arglist 位于 0xBFFFF1E8。它位于我的系统上——<code>argc 并且argv位于 little_array 之下。尝试p &argvp &argc确定它们的放置位置。

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

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

0xBFFFF458 是漏洞利用后存储的值 argv(注意argc与 相同(signed)0xBFFFF458 == -1073744808)。之前,argv持有 0xbffff294。在这两种情况下,argv自身都存储在其他地方。

于 2010-05-01T15:19:21.003 回答