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

一、 实验目标:

  1. 理解程序函数调用中参数传递机制;
  2. 掌握缓冲区溢出攻击方法;
  3. 进一步熟练掌握GDB调试工具和objdump反汇编工具。

二、实验环境:

  1. 计算机(Intel CPU)
  2. Linux 64位操作系统
  3. GDB调试工具
  4. 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。

        通过分析,可做如下栈帧示意图:
在这里插入图片描述

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

随着字符串变长,下面的信息会被破坏:
在这里插入图片描述

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

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

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

【测试结果】
        此时我们进行测试,使用管道将输入流重定向到bufbomb中:
在这里插入图片描述

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

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

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

【解题过程】
        首先观察fizz()的汇编代码:
在这里插入图片描述

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

对应到getbuf()函数中的栈帧结构如下:
在这里插入图片描述

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

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

        再利用makecookie生成自己的cookie值为642b7ea2 。
在这里插入图片描述

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

【测试结果】
        此时我们进行测试,使用管道将输入流重定向到bufbomb中:
在这里插入图片描述

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

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

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

【解题过程】
        首先,为了能精确地指定跳转地址,先在root权限下关闭Linux的内存地址随机化:
在这里插入图片描述

        观察bang()的汇编代码:
在这里插入图片描述

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

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

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

        不妨创建一个temp.s的文件用于存汇编代码:
在这里插入图片描述

        通过gcc编译该汇编代码,再反编译输出到temp.txt中。
在这里插入图片描述
在这里插入图片描述

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

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

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

【测试结果】
        此时我们进行测试,使用管道将输入流重定向到bufbomb中:
在这里插入图片描述

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

五、实验总结与体会

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

  • 寄存器的功能:
  • 寻址方式:
  • 操作指令
            通过本实验,我学习了利用缓冲区溢出漏洞对程序进行攻击的方法,对程序运行时的栈帧结构有了跟深层次的了解。也学会了通过使用gdb调试完成对程序的运行情况进行检查并获取程序运行过程中的值。
            此外,本次实验给我的启示是,在自己编写程序过程中,也需要尤为注意缓冲区溢出的情况,防范缓冲区溢出攻击。
  • 20
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
缓冲区溢出是一种计算机安全漏洞,攻击者通过在程序中输入超过缓冲区边界的数据,将恶意代码注入到程序中执行的过程。缓冲区溢出攻击可以导致程序崩溃、执行未经授权的代码,甚至获取系统权限。经过实验,可以更好地理解和防范这种攻击。 在level3: rumble的实验中,我们面临的是一个更高级的缓冲区溢出攻击。在这个实验中,攻击者通过输入特殊构造的数据,成功改变程序的运行流程,使得程序执行攻击者所期望的指令。这种攻击常常利用了程序中的函数指针或返回地址的问题。具体来说,攻击者会试图将恶意代码的地址写到函数指针或返回地址处,从而实现程序流的改变。 为了进行这个实验,我们首先需要了解目标程序的结构和存在的漏洞。然后,攻击者需要通过输入过长的数据,溢出缓冲区并覆盖到函数指针或返回地址。攻击者会通过调整输入数据的内容和长度,来逐步控制程序的执行流程,达到他们想要的目的。 为了防范和避免缓冲区溢出攻击,我们可以采取以下措施: 1. 输入验证和长度限制:限制输入的长度,避免超出缓冲区的边界。 2. 栈保护技术:使用栈保护技术,比如栈溢出检测和随机化布局,来使攻击者更难寻找正确的溢出点。 3. 代码审查:对程序进行审查,及时发现和修复潜在的缓冲区溢出漏洞。 4. 程序更新和修复:及时更新和修复软件和库,以防止已知的缓冲区溢出漏洞被攻击者利用。 总之,实验level3: rumble是一种对高级缓冲区溢出攻击的模拟,通过实验我们可以更加深入地了解缓冲区溢出的原理和防范措施,提高系统的安全性。同时,我们也应该意识到,缓冲区溢出攻击是一种严重的安全威胁,需要我们加强对软件和系统的安全管理和维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上山打老虎D

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值