20232412 2025-2026-1 《网络与系统攻防技术》实验一实验报告

1.实验内容

本次实验围绕一个存在缓冲区溢出漏洞的Linux程序pwn1展开,通过三种方式劫持其控制流,使其执行原本不会被调用的getShell函数从而获得系统Shell。

三种方式如下:

  • 通过直接修改可执行文件的机器指令,将调用 foo 的函数地址替换为 getShell 的地址
  • 利用 foo 函数的Bof漏洞,构造一个攻击输入字符串,覆盖其返回地址,跳转执行 getShell 函数
  • 将 Shellcode 注入到缓冲区,覆盖返回地址并运行该 Shellcode,从而跳转到 getShell

这三个实践由浅入深地揭示了程序运行机制、栈溢出原理及Shellcode攻击的基本过程,在实验开始前需要掌握一些基础知识:

  • Linux的基础操作

本次实验在Kali虚拟机上完成,我们需要掌握一些常用指令,如管道、输入、输出重定向,还要会用gdb,vi等工具,为虚拟机配置好工具能提高实验效率。

  • 汇编语言

实验中需要根据汇编指令来理解函数如何被调用和返回,从而明确在何处修改指令或是覆盖返回地址,我们可以通过反汇编程序,查看 getShell 和 foo 等函数的起始内存地址,阅读函数的汇编代码,确定输入缓冲区的起始位置。

  • Bof的原理

缓冲区溢出的核心原理是:程序向一个预定大小的缓冲区中写入了超出其容量的数据,这些多出的数据会超出缓冲区边界,覆盖到缓冲区相邻的内存区域,篡改栈上保存的“函数返回地址”。这样,当函数执行完毕准备返回时,便会跳转到这个被篡改后的地址去执行,而非原定的合法地址,这就是我们调取getShell函数的方式。

方式二、方式三虽然都利用了缓冲区溢出漏洞,但攻击思路和实现手却有区别:方式二通过溢出修改返回地址,直接跳转去执行程序内部已有的getShell函数,而方式三则是将一段能获取 Shell 的机器指令作为输入数据注入到内存中,再让程序跳转去执行这段我们自己注入的代码。

2.实验过程

2.1 修改可执行文件,改变程序执行流程

下载目标文件pwn1(之后更为pwn202324_1,之后的命名格式相同),输入objdump -d pwn1 | more反汇编。

810c12de957d6c8c6e7c4d73202d040e

我们可以看到 main 函数调用 foo,对应机器指令为e8 d7ffffff,我们需要修改该机器指令,让它调用 getShell,所有我们需要修改对应的指令。CPU会跳转执行 EIP + d7ffffff这个位置的指令,即 foo的地址08048491,而d7ffffff是补码,表示-41,我们可以计算得到 EIP 的值,并修改补码使其指向 getShell 的地址,通过计算可以知道修改补码为c3ffffff

以下为修改过程:

创建副本,用vi文本编辑器来打开文件

9957c581f3e6be02bf65320a7478d58a
f1ee6c2e43a0c0334b10fd85e59e8ef1

我们要将显示模式切换为16进制模式,输入%!xxd进行切换,,用/e8 d7找到要修改的内容,更改为e8 c3,再输入%!xxd -r换回原格式并存盘退出

ff27fbd74959a71db2fbf59b4d2a1f50
0afecbfcdd16d1ce400fb7d8eed2723d

我们再次反汇编,可以看到call指令可以正确调用getShell,输入./pwn20232412_1运行修改后的代码,可以发现返回了一个可用Shell,实验验证成功。

051a13cf5e8ccdbcfbe7277635965b3f
a1bdd6cd0c174d94c17699011a77f758

2.2 构造输入参数,造成BOF攻击,改变程序执行流

首先我们要输入字符串,确认哪几个字符会覆盖到返回地址。启动 gdb 调试器,加载可执行文件pwn20232412_2,并在 gdb 环境中运行,输入字符串后提示程序崩溃。

我们查看寄存器,原本指令指针寄存器 EIP 保存着 CPU 接下来要执行的指令的地址,但是这里却被0x35,即数字'5'的 ASCII 码覆盖,说明我们成功用输入数据覆盖了函数的返回地址。

0d0138a24ac1e8c300c20fe49b2cdd01

再次尝试,更改输入的字符串,我们可以确定是1234四个数最终会覆盖到堆栈上的返回地址,所以把这四个字符替换为 getShell 的内存地址,输给 pwn,pwn 就会运行 getShell。

9c42f2039b03004417762ce379fd7903

通过刚才的反汇编,我们已知 getShell的内存地址为0804847d,应输入字符串11111111222222223333333344444444\x7d\x84\x04\x08修改返回地址为 getShell 的内存地址,运行程序,可以发现返回了一个可用Shell,实验验证成功。

8009ad13947be5da07b873c454753207

2.3 注入Shellcode并执行

在开始攻击方式三前,我们需要下载工具 execstack 用于查询和修改文件的栈执行权限。

由于学习通提供的安装包在我的虚拟机中无法兼容,我直接在虚拟机中到镜像网站中下载安装包wget http://archive.debian.org/debian/pool/main/p/prelink/execstack_0.0.20130503-1.1+b5_amd64.deb

我们用 execstack 工具设置堆栈可执行,同时修改环境,输入echo "0">/proc/sys/kernel/randomize_va_space关闭地址随机化。

56432dd3af2c473b51a4949ae85e5150
7e4d311621c9ddedde762c93815485df

接下来构造要注入的payload,结构为anything+retaddr+nops+shellcode,计算可知要在返回地址之前填充32字节,所以 anything 应为'A'x32,返回地址只要落在任何一个nop上,就会滑到我们的 shellcode 上,我们要做的就是确认 retaddr 的值。输入perl -e 'print "A" x 32;print "\x04\x03\x02\x01\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\x00\xd3\xff\xff\x00"' > input_shellcode,我们需要确定\x4\x3\x2\x1该填什么。

打开一个终端注入这段攻击buf。

cf9ced43160ccee1f981b671d9ceccf7

再开另一个终端,先确定pwn的进程号,再用gdb来调试pwn,通过设置断点,来查看注入buf的内存地址

17605f2f8a4dbeb7a8b6a68a5170d0ad
dc8f8dcfd65b32c32790039f184566fe

在第一个终端中按回车,执行至断点处,查看 ESP 中的值,发现用来标记返回地址的0x01020304。因为内存地址是连续的,我们可以 shellcode 地址为0xffffd2f0

2f26233fb5595f742ce00c7696a5ca13

重新使用 Perl 生成一个含 shellcode 的文件,将0x01020304替换成0xf0d2ffff,注入这段攻击buf并运行进程,可以发现返回了一个可用Shell,实验验证成功。

e43445c2e640f3ad3e6184e913328eed

3.问题及解决方案

  • 问题1:学习通的 execstack 安装包无法正常安装

  • 问题1解决方案:经过多次尝试,根据报错提示下载补丁,但文件格式始终无法匹配,最后在虚拟机中通过镜像网站下载 execstack 的 deb 包,完成安装。

  • 问题2:注入 payload 未发现标记返回地址的特征值

  • 问题2解决方案:与同学交流后发现(cat input_shellcode;cat)| ./pwn./pwn的区别,我误以为从管道中读取到 input_shellcode 文件的内容会一直保存在进程中,我多次按回车后会退出被注入的pwn。我没发现pwn进程号后输入./pwn,即我用gdb调试的是没有注入的pwn文件,因此没发现标记返回地址的特征值。更正错误后便能按预期发现特征值。

4.学习感悟、思考等

本次实验让我系统理解了缓冲区溢出漏洞的原理与多种利用方法,其中 ShellCode 注入过程相比其他两种方式略显复杂,操作起来也更难。我原本不理解其攻击原理,只是照着实验参考书一步步模仿,出现问题后不懂得解决,后来我认真查询 ShellCode 基础知识,并结合AI为我画图说明,我才弄清楚注入后的堆栈结构,真正明白本次实验的三种攻击方式的原理。同时我也了解了相关的Bof防御技术,地址随机化让 OS 每次都用不同的地址加载应用,能够有效避免Bof攻击,攻防对抗的过程让我对本门课的学习产生了极大兴趣。

参考资料

逆向与Bof基础
初识shellcode

posted @ 2025-10-13 20:59  20232412李  阅读(7)  评论(0)    收藏  举报