计算机系统(2) 实验四 缓冲区溢出攻击实验

简介: 计算机系统(2) 实验四 缓冲区溢出攻击实验

一、 实验目标:


理解程序函数调用中参数传递机制;

掌握缓冲区溢出攻击方法;

进一步熟练掌握GDB调试工具和objdump反汇编工具。


二、实验环境:


计算机(Intel CPU)

Linux 64位操作系统

GDB调试工具

objdump反汇编工具


三、实验内容


       本实验设计为一个黑客利用缓冲区溢出技术进行攻击的游戏。我们仅给黑客(同学)提供一个二进制可执行文件bufbomb和部分函数的C代码,不提供每个关卡的源代码。程序运行中有3个关卡,每个关卡需要用户输入正确的缓冲区内容,否则无法通过管卡!


       要求同学查看各关卡的要求,运用GDB调试工具和objdump反汇编工具,通过分析汇编代码和相应的栈帧结构,通过缓冲区溢出办法在执行了getbuf()函数返回时作攻击,使之返回到各关卡要求的指定函数中。第一关只需要返回到指定函数,第二关不仅返回到指定函数还需要为该指定函数准备好参数,最后一关要求在返回到指定函数之前执行一段汇编代码完成全局变量的修改。


       实验代码bufbomb和相关工具(sendstring/makecookie)的更详细内容请参考“实验四 缓冲区溢出攻击实验.pptx”。


       本实验要求解决关卡1、2、3,给出实验思路,通过截图把实验过程和结果写在实验报告上。


四、实验步骤和结果


       首先利用反汇编命令查看getbuf函数的汇编代码,以便分析getbuf在调用时的栈帧结构,汇编代码如下:


(一)返回到smoke


【解题思路】

       本实验中,bufbomb中的test()函数将会调用getbuf()函数,getbuf()函数再调用gets()从标准输入设备读入字符串。


       系统函数gets()未进行缓冲区溢出保护。其代码如下:


int getbuf()
{
    char buf[12];
    Gets(buf);
    return 1;
}

我们的目标是使getbuf()返回时,不返回到test(),而是直接返回到指定的smoke()函数。


       为此,我们可以通过构造并输入大于getbuf()中给出的数据缓冲区的字符串而破坏getbuf()的栈帧,替换其返回地址,将返回地址改成smoke()函数的地址。


【解题过程】

       分析getbuf()函数的汇编代码,不难发现,getbuf()在保存%ebp的旧值后,将%ebp指向%esp所指的位置,然后将栈指针减去0x28来分配额外的40个字节的地址空间。字符数组buf的位置用%ebp下0x18(即24)个字节来计算。然后调用Gets()函数,读取的字符串返回到%ebp-0x18,即%ebp-24。


       通过分析,可做如下栈帧示意图:


caf49402ac1d496e8577379ba39f147d.png

从以上分析可得,只要输入字符比较短时,gets返回的字符串(包括末尾的‘\0’)就能够放进buf分配的空间里。而长一些的字符串就会导致gets返回字符串过长而覆盖栈上存储的某些信息。

随着字符串变长,下面的信息会被破坏:

cc274cb1988a490980e310030de7fa32.png

 因此,我们要替换返回地址,只需要构造一个长度至少为32的字符串,其中的第0~11个字符放进buf分配的空间里,第12~23个字符放进程序分配后未使用的空间里,第24~27个字符覆盖保存的%ebp旧值,第28-31个字符覆盖返回地址。


       由于替换掉返回地址后,getbuf()函数将不会再返回到test()中,所以覆盖掉test()的%ebp旧值并不会对程序有任何影响。因此构造的长度为32的字符串前28个字符可以为任意值,而后面四个字符为smoke()函数的地址。通过反汇编查看代码,可以发现,smoke函数的地址为08048eb0。


152581b5a0e5479cb9491b81e44f9a3a.png

由于我的学号是2019284073,因此,不妨构造如下攻击输入语句:

20192840732019284073201928407320192840732019284073000000b08e0408


【测试结果】

       此时我们进行测试,使用管道将输入流重定向到bufbomb中:


34666511f03d41828247225222621fdb.png


       答案正确,成功地调用了smoke函数。


(二)返回到fizz()并准备相应参数


【解题思路】

       这一关要求返回到fizz()并传入自己的cookie值作为参数,破解的思路和第一关是类似的,构造一个超过缓冲区长度的字符串将返回地址替换成fizz()的地址,只是增加了一个传入参数,所以在读入字符串时,要把fizz()函数读取参数的地址替换成自己的cookie值,具体细节见解题过程。


【解题过程】

       首先观察fizz()的汇编代码:


94eeb8c44a714b349a456d96b3c88e9d.png

 

  从汇编代码可知,fizz()函数被调用时首先保存%ebp旧值并分配新的空间,然后读取%ebp-0x8地址处的内容作为传入的参数,要求传入的参数是自己的cookie值。也就是说传入的参数其实是存在%ebp-0x8处的,具体的栈帧结构如下:


c9ca67004c1d4a5aa061a5e027c54151.png


对应到getbuf()函数中的栈帧结构如下:


2a39b187c22e48e6bd4b201af553a068.png


       由以上结构不难判断出,我们需要读入buf的字符串为“28个任意字符+fizz()的地址+4个任意的字符+自己的cookie值”。


       通过反汇编,可以知道fizz()的地址为0x08048e60。


       再利用makecookie生成自己的cookie值为642b7ea2 。


e3d3daecee9f4e9a86cb69881f7442c1.png


       由于我的学号是2019284073,因此,不妨构造如下攻击输入语句:

20192840732019284073201928407320192840732019284073000000608e040800000000a27e2b64


【测试结果】

       此时我们进行测试,使用管道将输入流重定向到bufbomb中:


ba5029ea059e45c2a38fd843831502a4.png

       答案正确,成功地调用了fizz函数。


(三)返回到bang()且修改global_value


【解题思路】

       这一关要求先修改全局变量global_value的值为自己的cookie值,再返回到band()。为此需要先编写一段代码,在代码中把global_value的值改为自己的cookie后返回到band()函数。将这段代码通过GCC产生目标文件后读入到buf数组中,并使getbuf函数的返回到buf数组的地址,这样程序就会执行我们写的代码,修改global_value的值并调用band()函数。具体细节见解题过程。


【解题过程】

       首先,为了能精确地指定跳转地址,先在root权限下关闭Linux的内存地址随机化:


316c335232364c0884915605980b8a74.png

       观察bang()的汇编代码:


245de1305f7a4d6489378cd6f080553f.png


       很明显,bang()函数首先读取0x804a1c4和0x804a1d4的地址的内容并进行比较,要求两个地址中的内容相同,我们不妨使用gdb查看对应地址的值:


526ba0bad4e64c9ca6233fecafb8a202.png


       可以发现,0x804a1c4就是全局变量global_value的地址,0x804a1d4是cookie的地址。因此,我们只要在自己写的代码中,把地址0x804a1d4的内容存到地址0x804a1c4即可。通过反汇编,可以获得bang()函数的入口地址为0x08048e10。


       此时,可以确定我们自己写的代码要干的事情了。首先是将global_value的值设置为cookie的值,也就是将0x804a1c4的值设置为0x804a1d4的值,然后将bang()函数的入口地址0x08048e10压入栈中,这样当函数返回的时候,就会直接取栈顶作为返回地址,从而调用bang()函数。接着函数返回,此时返回的地址就是上一条语句中压入栈中的地址,也就是bang()函数的入口地址了。


       不妨创建一个temp.s的文件用于存汇编代码:


5343d3049dbb446e8be6145f25a186f1.png


       通过gcc编译该汇编代码,再反编译输出到temp.txt中。


4301b400a000491bb848e142a1e489de.png

942c41c62c2d4730a33629bc1cb8c88a.png


       在将这段机器码放入buf数组中后,为了能让getbuf()返回到buf数组处执行我们的代码,需要想办法得到buf数组的地址。为此,用gdb断点调试查看执行getbuf()时ebp的值为0xffffbe28。


44621d94a5944f13b6295bc5c461da29.png


       从第一关中对getbuf()函数栈帧结构的分析可知buf数组的首地址为%ebp-0x18,即0xffffbe18。


       综上所述,最后我们要构造的字符串为自己写的汇编代码生成的机器码(20个字符)+8个任意字符+buf数组的首地址,则对应的字符串为:

488b1425d4a1040848891425c4a1040848c7c2108e0408ffe200000010beffff


【测试结果】

       此时我们进行测试,使用管道将输入流重定向到bufbomb中:


a14576e299e24601849d9f3c56162373.png


       答案正确,成功地将golbal_value设置为了我的cookie。


五、实验总结与体会


本次实验中,复习了之前学过的很多知识:


寄存器的功能:

寻址方式:

操作指令

       通过本实验,我学习了利用缓冲区溢出漏洞对程序进行攻击的方法,对程序运行时的栈帧结构有了跟深层次的了解。也学会了通过使用gdb调试完成对程序的运行情况进行检查并获取程序运行过程中的值。

       此外,本次实验给我的启示是,在自己编写程序过程中,也需要尤为注意缓冲区溢出的情况,防范缓冲区溢出攻击。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
2月前
|
SQL 安全 网络协议
黑盒渗透测试:揭开未知的安全隐患
【8月更文挑战第31天】
95 0
|
SQL 网络安全 数据库
网络安全实验十 sql注入实验(一)
网络安全实验十 sql注入实验(一)
350 0
|
SQL 网络安全 数据库
网络安全实验十一 sql注入实验(二)
网络安全实验十一 sql注入实验(二)
89 0
网络安全实验十一 sql注入实验(二)
|
安全 网络协议 JavaScript
网络安全实验九 恶意代码实验
网络安全实验九 恶意代码实验
307 0
|
安全 API Windows
恶意病毒逆向分析实验1
恶意病毒逆向分析
|
存储 NoSQL 前端开发
计算机系统(2) 实验三 逆向工程实验
计算机系统(2) 实验三 逆向工程实验
230 0
计算机系统(2) 实验三 逆向工程实验
|
安全 计算机视觉 Windows
【知识】缓冲区溢出攻击基础+靶机演练
缓冲区溢出是常见的应用或系统漏洞,本文适合渗透初学者进阶,为了更有利于小伙伴的学习,我录制了相关视频,发布到bilibili,值得一看。
404 0
|
存储 安全 网络协议
如何处理网络渗透测试结果
如何处理网络渗透测试结果
183 1
|
SQL 开发框架 缓存
内网渗透之域环境渗透测试过程
申明:此次渗透环境为实验环境,仅供渗透实验参考
225 0
内网渗透之域环境渗透测试过程
|
安全 异构计算
研究者展示黑客通过GPU绕过防毒软件查杀的方法
Slashdot网站近日刊登了一篇研究文章称黑客可以通过特殊手段利用电脑中的GPU来躲避杀毒软件的清查。在这份PDF文档中,研究者展示了黑 客如何 利用GPU来躲避杀毒软件查杀的方法,作者还表示类似的技巧还有可能会发展升级为更加复杂的攻击手段,如利用GPU来扫描用户屏幕上所显示的各个像素图 像,并借机窃取用户的机密信息等等。
692 0