实验题目:CS:APP Buflab

实验目的:

This assignment will help you develop a detailed understanding of IA-32 calling conventions and stack organization.
Our purpose is to help you learn about the runtime operation of programs and to understand the nature of this form of security weakness so that you can avoid it when you write system code.

实验环境:VMware 下 Ubuntu32位

快速索引:
level 0
level 1
level 2
level 3
level 4

操作步骤:

开始level级任务之前,根据userid(这里使用Ubuntu系统的$USER:mosh)查看自己的cookie:
CS:APP Buflab 缓冲区溢出攻击实验
为了便于阅读汇编代码,将反汇编出来的代码重定向输出到文件中:
$ objdump -d bufbomb > bufbomb.s

level 0

问题分析:
Level0的任务是要从getbuf函数入手,不返回test函数而是直接跳转到smoke函数处去执行。
需要我们输入攻击字符串利用缓冲区溢出将getbuf栈帧中存放的返回地址修改为smoke函数的首地址。

解决过程:

观察getbuf函数的汇编代码:
CS:APP Buflab 缓冲区溢出攻击实验
可知开始存放输入字符串的位置为-0x28(%ebp)也就是%ebp往下40个字节的位置。考虑到%ebp处存放的为旧%ebp值,0x4(%ebp)处存放的是返回地址,所以构建的字符串可以是48个字节,前44个字节可以是除0x0A外任意值,最后4个字节为smoke函数的起始地址,但是存放时要注意按照小端法则来存放。
查看smoke函数首地址
CS:APP Buflab 缓冲区溢出攻击实验
由于字符串中不能出现0x0a(代表‘\n’,会被当做字符串结束的换行符),所以可以取下一条指令地址0x8048e0b。
构造攻击字符串如下:
CS:APP Buflab 缓冲区溢出攻击实验
运行结果:
CS:APP Buflab 缓冲区溢出攻击实验

Level1

问题分析:
CS:APP Buflab 缓冲区溢出攻击实验
本任务要求不返回test函数而跳转到fizz函数执行,并输出相应语句。需要我们在修改返回地址的同时将参数cookie放到合适的栈帧位置上。

解决过程:
CS:APP Buflab 缓冲区溢出攻击实验
下面来分析一下函数的栈帧:getbuf函数执行完ret指令(pop %eip)后,esp指向返回地址的上方,eip中存储的为下一条指令地址—fizz函数首地址,跳转到fizz函数执行后,首先将旧ebp压栈,而后将ebp与esp拉平,再下拉esp创建fizz函数的栈帧,紧接着要取参数进行比较。参数放置于0x8(%ebp)的位置,结合上述分析,参数存放在返回地址上方4个字节处。也就是说,设计的攻击字符串总共56个字节,前44个字节为除0x0A外的任意值,44-47字节为fizz函数首地址,48-51字节也可以是任意值,最后4个字节为cookie值作为fizz函数的参数。
栈帧变化示意图:
CS:APP Buflab 缓冲区溢出攻击实验
设计攻击字符串如下:
CS:APP Buflab 缓冲区溢出攻击实验
运行结果:
CS:APP Buflab 缓冲区溢出攻击实验

Level2

问题分析:
CS:APP Buflab 缓冲区溢出攻击实验
Level2在level1的基础上稍作修改,level1中是让函数的参数等于cookie值,我们可以通过在栈帧相应参数位置中放上cookie值即可。而level2是要让一个全局变量的值等于cookie,仅仅通过攻击字符串就无法实现了,而要设计攻击代码去完成相应操作。
此时要将返回地址修改为getbuf栈帧中开始存放攻击字符串的位置,攻击字符串的内容为我们设计的指令字节码。getbuf函数执行ret指令后,程序跳转到字符串开始位置执行我们设计的指令。

解决过程:

首先通过gdb调试(设置断点在getbuf函数)找到字符串缓冲区的起始位置。
CS:APP Buflab 缓冲区溢出攻击实验
上图为getbuf函数栈帧的%ebp,所以存放字符串的位置为 -0x28(%ebp) = 0x55683270 - 0x28 = 0x55683248
CS:APP Buflab 缓冲区溢出攻击实验
观察bang函数汇编代码,首先记录其函数首地址,然后可以判断global_value的地址为圈出的两个地址其一,可以通过gdb调试来确定。
CS:APP Buflab 缓冲区溢出攻击实验
接下来要设计待执行的指令,完成对global_value的赋值和之后到bang函数的跳转。为了更快捷地获取指令字节码,可以先写好汇编代码,经过gcc编译成可重定位目标文件后再使用objdump反汇编查看。
CS:APP Buflab 缓冲区溢出攻击实验
所以输入字符串可就应该设计为:前面一些字节为指令字节码,最后4个字节为48 32 68 55即getbuf函数栈帧中字符串开始存放的位置,中间的字节用任意内容补齐(整个攻击字符串的长度要满足正好将返回地址覆盖,中间字节的长度要根据指令字节码的长度确定)。
设计攻击字符串如下:
CS:APP Buflab 缓冲区溢出攻击实验
运行结果如下:
CS:APP Buflab 缓冲区溢出攻击实验

Level3

问题分析:

Level3的目的是要我们由getbuf函数返回到test函数,并且要修改其返回值使之能执行相应语句,使程序能够正常终止,看起来好像什么都没有发生
CS:APP Buflab 缓冲区溢出攻击实验
显然也要通过设计攻击指令的方式来完成。攻击指令要完成两件事:将getbuf函数返回值修改为cookie值,确保函数可以正常返回test函数执行。
其中修改cookie值较为简单,使用mov指令操作%eax即可。要使函数正常返回,就要恢复被我们破坏的栈帧:恢复返回地址和旧ebp。
对于返回地址的恢复,可以使用ret(pop %eip)指令来完成:在修改完getbuf函数返回值后,先用push指令将test函数中返回压栈,紧接着使用ret指令弹栈到eip即可。
对于旧ebp的恢复可以有多种方案:
方案一:由于在设计攻击字符串时,要将返回地址覆盖为getbuf函数栈帧中开始存放字符串的位置,所以旧ebp的值也要被覆盖。由于这里需要恢复test函数的ebp,我们可以选择保留旧ebp值,具体的做法是现在gdb调试中确定test函数的ebp值,将其写到攻击字符串的41-44字节中。
方案二:设计攻击字符串时无需考虑旧ebp的覆盖值,而选择在攻击指令中完成对ebp的修复。使用mov x, %ebp的形式来完成。对于x旧ebp值,既可以实现通过gdb调试记录;也可以根据汇编代码确定%ebp和%esp的位置关系,由esp值反推。

解决过程:

这里对旧ebp的恢复采用方案一来实现。
进入gdb调试,设置断点,查看%ebp处存放的数据信息:
CS:APP Buflab 缓冲区溢出攻击实验
可以看到在缓冲区溢出之前,返回地址为0x8048e50,test函数%ebp为0x556832a0
接下来编写攻击指令并获取其字节码:
CS:APP Buflab 缓冲区溢出攻击实验
编写攻击字符串如下:
CS:APP Buflab 缓冲区溢出攻击实验
运行结果:
CS:APP Buflab 缓冲区溢出攻击实验

Level4

问题分析:

在level3中,由于在getbuf函数中加入了一些稳定栈帧的特性,运行时其栈帧位置相对固定,我们从gdb调试中获取的栈帧位置信息才能有效。但在本级任务中,运行时getbufn函数的栈帧位置将发生改变,其%ebp值可能会相差±240。在此基础上,我们要通过 -n 选项来完成连续5次从getbufn函数修改返回值后正常返回testn函数并输出相应字符的操作。
根据题目提示,“空操作雪橇”(nop sled)成为解决本题的关键:
CS:APP Buflab 缓冲区溢出攻击实验
尽管函数栈帧的地址相对随机化,但是我们可以在实际攻击代码前加上很长一段nop指令,在指定指令执行开始位置后,程序将会“滑过”这一段nop指令,最终来到攻击指令处。这里nop指令的长度和指定的开始执行位置都是需要根据已知条件进行计算的。

解决过程: CS:APP Buflab 缓冲区溢出攻击实验
CS:APP Buflab 缓冲区溢出攻击实验
通过gdb调试可知,其中一种运行情况下getbufn函数的%ebp为0x55683270,结合getbufn函数汇编代码可知此情况下开始存放字符串的位置为0x55683270 - 0x208 = 0x55683068。结合题目所给信息不同运行之间%ebp可能相差±240(0xf0),所以推知不同运行下字符串的开始位置范围为0x55683068±0xf0。
也就是说,在指定跳转到的运行地址为存放攻击字符串位置的上限(0x55683068 + 0xf0 = 0x55683158)时,只需要480个nop指令,就一定可以滑到攻击指令处。
下面编写攻击指令:由于%ebp的值不再固定,故只能采用level3中的方案2来解决
CS:APP Buflab 缓冲区溢出攻击实验
观察testn函数汇编代码可知,%esp和%ebp位置相差0x24 + 4 = 0x28个字节,所以就可以根据%esp值来反推%ebp值。
编写攻击指令并获取指令字节码:
CS:APP Buflab 缓冲区溢出攻击实验
编写攻击字符串如下:
CS:APP Buflab 缓冲区溢出攻击实验
运行结果:

CS:APP Buflab 缓冲区溢出攻击实验

相关文章: