20232311 2025-2026-1 《网络与系统攻防技术》实验一实验报告
一、实验内容
本周围绕Linux可执行文件pwn1的缓冲区溢出(BOF)漏洞与shellcode注入展开学习,核心是通过三种技术手段篡改程序执行流程,触发原本不可运行的getShell函数或自定义shellcode。具体包括:手动修改可执行文件的机器指令,直接将main函数调用目标从foo改为getShell;利用foo函数的BOF漏洞,构造攻击字符串覆盖返回地址,间接触发getShell;在关闭系统防护机制后,注入自定义shellcode并实现其运行,最终获取交互式Shell。
整个学习过程不仅涉及汇编指令、栈结构等底层知识,还需熟练使用gdb调试、vi十六进制编辑、patchelf等工具,理解小端字节序、地址随机化、堆栈权限等关键概念。
二、实验过程
1、直接修改程序机器指令,改变程序执行流程
(1)下载目标文件pwn1,反汇编分析关键代码:
首先获取目标文件pwn1并进行反汇编,重点分析main、foo与getShell三个函数的地址及调用关系。反汇编结果显示,getShell函数起始地址为0x0804847d,foo函数起始地址为0x08048491,main函数中调用foo函数的指令位于地址0x080484b5,对应的机器指令为e8 d7 ff ff ff。
其中,e8为调用指令(call)的操作码,后续的d7 ff ff ff是相对偏移量(补码形式,对应十进制 - 41)。根据 call 指令的执行逻辑,CPU 会将当前指令下一条地址(0x080484ba)与该偏移量相加,得到目标函数地址(0x080484ba - 0x29 = 0x08048491),即foo函数地址。若要让main函数调用getShell,需将该偏移量修改为getShell 地址 - 0x080484ba对应的补码。通过计算,0x0804847d - 0x080484ba = -0x3d,其补码为c3 ff ff ff。
(2)修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
1)为避免破坏原文件,先复制pwn1生成新文件pwn2311,使用vi编辑它:
cp pwn20232311 pwn2311
vi pwn2311
2)将显示模式转换为16进制模式,便于定位和修改机器指令:%!xxd
3)搜索目标指令e8 d7,找到main函数中调用foo的机器指令位置,将偏移量部分的d7修改为c3
4)输入:%!xxd -r将十六进制格式还原为可执行文件格式,保存并退出vi,再反汇编看一下call指令已经正确调用getshell
5)运行修改后的文件,成功获取Shell提示符,表明程序执行流程已成功指向getShell 函数
2、通过构造输入参数,造成BOF攻击,改变程序执行流
(1)反汇编,了解程序的基本功能,分析漏洞函数
重新反汇编pwn1,聚焦foo函数的内存操作逻辑。foo函数中通过sub $0x38, % esp分配栈空间,再通过lea -0x1c (% ebp), % eax将缓冲区起始地址传入 gets 函数。由于 gets 函数无输入长度限制,而缓冲区仅预留 28 字节(0x1c)空间,当输入字符串长度超过28字节时,多余数据会覆盖栈中后续存储的ebp寄存器值及函数返回地址,从而引发BOF漏洞。
同时,main函数调用foo函数时,会将返回地址(0x080484ba)压入栈中,该地址正是后续攻击需覆盖的关键目标。
(2)确认输入字符串哪几个字符会覆盖到返回地址
先给pwn2023211提权
进行确认哪几个字符会覆盖到返回地址,启动gdb调试 pwn1,运行程序并输入测试字符串1111111122222222333333334444444455555555,程序因内存访问错误(SIGSEGV)崩溃,查看寄存器状态(info r),发现eip寄存器值为 0x35353535(对应字符5的 ASCII 码),说明第33-36字节的输入覆盖了返回地址;
再次测试,输入1111111122222222333333334444444412345678,崩溃时eip值为0x34333231(对应字符1234),进一步确认输入字符串的第33-36字节会覆盖栈中的返回地址。
(3)确认用什么值来覆盖返回地址
getShell的内存地址,通过反汇编时可以看到,即0804847d。
接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。
对比之前eip 0x34333231 0x34333231,正确应用输入11111111222222223333333344444444\x7d\x84\x04\x08。
(4)构造输入字符串
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先利用perl语言生成包含攻击字符串的文件。其中,前32字节为填充字符,第33-36字节为反转后的getShell地址,最后\x0a为换行符,用于触发gets函数读取结束。
使用16进制查看指令xxd查看input文件的内容如同预期。
将input的输入,通过管道符“|”,作为pwn20232311的输入。达到预期成果。
4、注入Shellcode并执行
shellcode就是一段机器指令(code)通常这段机器指令的目的是为获取一个交互式的shell
(1)准备工作,修改一些设置:
设置堆栈可执行并验证
关闭地址随机化,避免shellcode地址被随机化导致无法定位
(2)构造要注入的payload。
采用 “填充字符 + 覆盖返回地址 + NOP sled + shellcode” 的payload结构,其中:填充字符:32字节,用于填满缓冲区并覆盖ebp;覆盖返回地址:需设置为 NOP sled 区域的地址,确保 CPU 执行到该地址时能滑入shellcode;NOP sled:多个\x90(NOP 指令),用于提高shellcode地址命中概率;shellcode:自定义的Shell获取指令序列。
初步生成payload文件:下面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。现在要确定\x4\x3\x2\x1到底该填什么。
1)打开一个终端注入攻击buf:
2)再开另外一个终端,先找到进程号:
3)启动gdb调试这个进程,通过设置断点,来查看注入buf的内存地址
4)反汇编 foo 函数找到返回指令(ret)地址(0x080484ae),设置断点:break *0x080484ae;在第一个终端中按下回车,触发断点,查看栈布局(x/16x esp),找到临时占位地址“\x01\x02\x03\x04”,确定其相邻的 NOP 区域地址
5)可以找到01020304了,就是返回地址的位置。shellcode就挨着,所以地址是0xffffd440
6)将返回地址改为0xffffd440,使用16进制查看指令xxd查看文件的内容如同预期。并且也能正确体现shellcode。
三、问题分析
1、问题1:
将显示模式切换为16进制模式时 :%!xxd 返回是command not found :xxd。并且执行sudo apt install xxd后依然报错。
问题1解决方案:查阅资料得知是默认的 Kali 官方源在当前网络环境中不稳定或版本不同步。于是便更换为国内的 Kali 镜像源并导入缺少的阿里云 Kali 源的 GPG 验证公钥。之后就可以顺利安装了
2、问题2:
按照常规方法尝试使用 execstack 工具设置堆栈可执行时,发现系统中未安装该工具,且通过 apt 安装时提示 “无法定位软件包 prelink”(execstack 依赖 prelink),导致堆栈权限设置受阻。
问题2解决方案:查阅资料后,选择使用功能类似且更易获取的 patchelf 工具替代 execstack。通过sudo apt install patchelf安装工具后,执行patchelf --set-execstack ~/Desktop/pwn4成功将 pwn4 的堆栈设置为可执行,满足实验需求。上文中有体现。
四、学习感悟
这次实验让我对网络攻防的底层原理有了更直观的认识,不再是停留在理论层面的 “缓冲区溢出”“shellcode” 等概念,而是真正动手操作时感受到每一个字节、每一条指令的重要性。比如修改机器指令时,偏移量的计算错误会导致程序直接崩溃;构造BOF攻击时,返回地址的字节序搞反会让攻击完全失效,这些细节让我明白攻防技术需要极致的严谨。
另外,工具的使用也是本次学习的重要收获。从gdb调试定位漏洞、vi编辑二进制文件,到perl生成攻击payload,每一款工具都有其独特作用,熟练掌握它们是开展攻防实验的基础。遇到工具安装问题时,通过查找资料更换替代工具(如patchelf替代execstack),也锻炼了我的问题解决能力,让我明白灵活变通在技术实践中的重要性。