1.实验内容
1.1手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
1.2利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
1.3注入一个自己制作的shellcode并运行这段shellcode。
2.实验目的
2.1掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码。
2.2掌握反汇编与十六进制编程器。
2.3能正确修改机器指令改变程序执行流程。
2.4能正确构造payload进行bof攻击。
3.实验环境
由于下载Kali系统所需时间过长,本次实验在华为云上购买的20.04版本Ubuntu虚拟机上进行,并安装了Kali工具。该虚拟机的配置信息如图一所示:
该虚拟机为64位操作系统,需要在其上启用32位架构支持,可使用命令dpkg --add-architecture i386实现。此后需使用命令apt install -y libc6:i386 libncurses5:i386 libstdc++6:i386安装32位库,如图2所示:
随后需要安装安装Kali工具链,通过命令echo "deb http://http.kali.org/kali kali-rolling main contrib non-free" > /etc/apt/sources.list.d/kali.list`,wget https://archive.kali.org/archive-key.asc,apt-key add archive-key.asc添加Kali源。然后可通过命令apt install -y metasploit-framework gdb nmap python3-pip,pip3 install pwntools安装Kali工具,如图3所示:
安装完成后可正式开始实验。
4.实验过程与分析
本实验通过三种方法获得shell,分别是直接修改程序的机器指令,跳转到getShell函数;利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数;注入一个自己制作的shellcode并执行。
4.1方法一:直接修改程序机器指令
4.1.1文件上传与准备
下载目标文件pwn1,通过WinSCP传入虚拟机中,并进行复制、改名,如图4所示:
此处为了确保pwn文件具有执行权限,需要使用命令chmod +x pwn1为它赋予执行权限,此后再进行复制、改名操作。这样所有复制得到的文件都将具有执行权限。如图5所示:
4.1.2计算返回地址
使用objdump -d pwn20232409_2 | more命令,对文件pwn20232409_2进行反汇编,并找到函数调用的相关指令。此处可以看到main中80484b5位置是跳转到foo函数的指令。如图6所示:
此处call 8048491的含义是这条指令将调用位于地址8048491处的foo函数。其对应机器指令为e8 d7 ff ff ff,e8表示跳转,d7 ff ff ff表示foo函数的地址。因此我们需要修改d7 ff ff ff为getShell函数的地址,使其直接调用getShell函数。
这里d7 ff ff ff为补码,而80484ba+d7ffffff=8048491。以此类推,为了获得getShell的地址,则804847d-80484ba=ffffffc3。因此我们需要将命令e8 d7 ff ff ff改为e8 c3 ff ff ff。
4.1.3修改返回地址
通过命令vi pwn20232409_2进入文件,按ESC键后输入:%!xxd将显示模式切换为16进制模式。通过/d7ff全局搜索d7ff找到需修改的位置,将其修改为c3ff。再通过命令:%!xxd -r转换16进制为原格式,存盘退出。如图7、图8所示:
4.1.4检验并执行文件
再次对文件pwn20232409_2进行反汇编,可以看到原先调用foo函数的指令已经调用的是getShell函数。如图9所示:
运行修改后文件pwn20232409_2,可以进入shell。如图10所示:
4.2方法二:Bof攻击,改变程序执行流
4.2.1通过反汇编,了解程序的基本功能
对文件pwn20232409_1(未修改的文件)进行反汇编,观察foo函数存在的漏洞。经检查发现,在8048497位置的“lea -0x1c(%ebp),%eax”指令只为后续的读入字符串预留了0x1c字节(即28字节)的缓冲区,因此foo函数存在缓冲区溢出漏洞。如图11所示:
4.2.2确定输入的位置及字符
使用gdb工具进行调试,在调试过程中往foo函数中输入超过28字节的字符串,查看各个寄存器的值,确定需要输入的地址数据。如图12所示:
在调试过程中,尝试输入字符串1111111122222222333333334444444412345678,发现寄存器eip的值变为0x34333231,即得到了输入1234字符串。于是我们可以得知1234字符串所在的位置最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。因此只需将这四个字符替换为 getShell 的内存地址,输给pwn文件,即可执行getShell函数。如图13所示:
由原先反汇编的结果可知,此处填入的字符串应为0x0804847d,如图14所示:
4.2.3构造输入的字符串
由为无法通过键盘直接输入\x7d\x84\x04\x08这样的16进制值,所以使用命令perl生成包括这样字符串的一个文件。随后使用xxd命令查看文件内容是否正确。如图15所示:
4.2.4执行文件
将文件input20232409通过管道符作为pwn20232409_1的输入,可以发现程序可以调用getShell函数,从而获得了一个shell。如图16所示:
4.3方法三:注入Shellcode并执行
4.3.1环境准备
为了使得实验成功,此处需要满足以下五个条件:关闭堆栈保护;关闭堆栈执行保护;关闭地址随机化;在32位操作系统环境下;在Linux环境中。后两个条件在配置环境时已经满足,现需满足前三个条件。
首先安装execstack命令行工具,用于查询和修改可执行文件或共享库的堆栈执行权限。使用命令apt install execstack即可安装。如图17所示:
通过命令execstack -s pwn1,修改可执行文件的堆栈执行权限,将堆栈设置可执行,使用命令execstack -q pwn1查询文件的堆栈可执行情况,若返回X,则说明可执行;
通过命令more /proc/sys/kernel/randomize_va_space来控制地址空间布局随机化(ASLR)的级别。其中返回值2表示完全开启 ASLR,即堆栈、堆、库文件的基址都会随机化;返回值1表示部分开启 ASLR,即仅随机化堆栈、堆、库文件的基址,但 mmap 基址固定;返回值0表示完全关闭 ASLR,即所有地址固定。
通过命令echo "0" > /proc/sys/kernel/randomize_va_space 将0写入到 /proc/sys/kernel/randomize_va_space 文件。用于完全关闭ASLR,使得程序每次运行时,其堆栈、堆、库文件的地址都是固定的。如图18所示:
4.3.2构造输入的payload
由于缓冲区有28字节,属于比较大的缓冲区,所以此处采用nop(填充)+shellcode+retaddr(缓冲区)这一构造,将shellcode放在缓冲区的前面。
此处构造了一个文件作为pwn20232409_1的输入。shellcode的地址暂时使用\x4\x3\x2\x1占位,后续分析得到shellcode的地址后将其替换。如图19所示:
打开另外一个终端,用gdb来调试pwn20232409_1这个进程,此处需要先找到进程的ID号,即PID,再根据其进行调试。如图20所示:
通过对函数foo进行反汇编,可以发现可以发现foo的结束地址是0x080484ae,在此处设置一个断点。如图21所示:
当程序运行到断点时,我们发现可以发现esp寄存器的内容为0xffffd54c.如图22所示:
使用命令以16进制形式显示从地址0xffffd54c开始的一部分数据,发现此处有注入的0x01020304,这就是返回地址的位置。而shellcode的地址则是0xffffd54c+4=0xffffd550。如图23所示:
于是应当将原先的\x4\x3\x2\x1更换为\x50\xd5\xff\xff。新构造的输入文件如图24所示:
4.3.3执行文件
将文件input_shell_20232409_2通过管道符作为pwn20232409_1的输入,可以发现程序可以调用getShell函数,从而获得了一个shell。如图25所示:
4.3.4结合nc模拟远程攻击
使用两台互相连通的Linux主机做,主机1模拟一个有漏洞的网络服务,主机2连接主机1并发送攻击载荷。此处的主机1与主机2均为我在华为云平台上购买的Ubuntu虚拟机,安装了Kali工具。其中主机一名为biyouchen,主机2名为biyouchen2。两台主机的内网地址通过ifconfig可以查看,分别为192.168.0.120与192.168.0.58,二者位于同一个网段下。如图26、27所示:


主机1模拟一个有漏洞的网络服务,并启动监听。利用主机2发送攻击载荷,成功获取了shell。如图28、29所示:


5.问题及解决
问题一:由于Kali镜像下载时间过长,我最初尝试使用Ubuntu24.04 64位镜像尝试实验,但发现pwn文件无法打开。错误原因为:该ELF文件缺少运行时所需的动态链接库,或者文件架构与当前系统不匹配。尝试安装链接库失败。
解决一:通过询问AI,我得知它的错误原因是由于24.04版本的Ubuntu太高级无法支持安装链接库。于是我又购买了Ubuntu20.04 64位镜像,在其上成功安装了32位库以及Kali工具链,顺利继续进行实验。
问题二:在shellcode注入过程中,我修改了对应输入文件后将其作为输入,没有获得shell,而是提示为段错误。
解决二:我仔细阅读gitee上的教程后发现,Linux下有两种基本构造攻击buf的方法,一种是retaddr+nop+shellcode,即把shellcode放在了缓冲区的后面;另一种是nop+shellcode+retaddr,即把shellcode放在了缓冲区的前面。我最初在查找需要修改的地址与最后修改时二者没有对应,导致明明使用了shellcode在缓冲区在前的方法却将其地址修改在了后面,导致跳转出错,出现了段错误。修改了需调整的地址后,得到了shell。
6.心得体会
本次实验我学习了逆向及Bof基础,学习的内容有:掌握汇编指令的机器码、掌握反汇编与十六进制编程器、修改机器指令改变程序执行流程、构造payload进行bof攻击。通过直接修改机器指令、进行bof攻击改变程序执行流、注入shellcode三种方式进行了实验。通过进行实验,我对于课上所学的缓冲区溢出的知识有了更深的体会。
实验初期,我因使用Ubuntu 24.04系统遇到了32位ELF文件兼容性问题,动态链接库安装屡屡失败。通过咨询AI得知高版本系统对旧库支持不足后,更换为Ubuntu 20.04并配置32位环境与Kali工具链。这让我深刻认识到开发环境对底层实验的重要性。
在实验过程中,我通过反汇编分析机器码,掌握了一些必要指令的十六进制编码,成功修改返回地址跳转到getShell函数,直观理解了程序执行流程的控制机制。同时,我也遇到了一些问题,比如我因混淆两种攻击构造方法(retaddr+nop+shellcode与nop+shellcode+retaddr),导致在shellcode注入中出现了地址跳转的错误。经反复调试并对照教程,才明确需将shellcode置于缓冲区前部并正确覆盖返回地址。这让我对攻击过程理解更加深入。
总而言之,通过本次实验,我学习了汇编指令与反汇编技术,体验了获取shell的诸多方法。我相信本次的实验会对我未来在网络与系统攻防技术的学习有所帮助。
参考资料
0x11_MAL_逆向与Bof基础.md