20232304 2025-2026-1 《网络与系统攻防技术》实验一实验报告
1.实验内容
本次实验以 Linux 可执行文件pwn1
为对象,核心目标是通过三种技术手段触发程序中默认不执行的getShell
函数(获取交互式 Shell),同时掌握底层攻防相关技能。具体内容包括:
-
手工修改
pwn1
的机器指令,直接改变程序执行流程,让main
函数调用getShell
而非默认的foo
函数; -
利用
foo
函数的缓冲区溢出(BOF)漏洞,构造攻击字符串覆盖堆栈中的返回地址,间接触发getShell
; -
编写并注入自定义
shellcode
(机器指令段),通过 BOF 漏洞执行注入的代码以获取 Shell。实验过程中需掌握关键技能:NOP/JNE/JE/JMP/CMP 等汇编指令的机器码、反汇编工具(
objdump
)与十六进制编辑器的使用、程序执行流程修改逻辑、BOF 攻击中payload
构造方法。
2.实验过程
2.0 实验环境准备
- 实验环境:Kali官网下载的VMWare虚拟机,使用hostname命令更改终端名
图2.0.1 虚拟机环境
- 将给出的pwn1文件复制到虚拟机中,并更名为pwn20232304
图2.0.2 文件更名过程
2.1 直接修改机器指令,改变程序执行流程
-
理论计算
对pwn1使用
odjump
工具进行反汇编,得到的结果如下所示:
图2.1.1 运行odjump对pwn1可执行文件进行反汇编
图2.1.2 反汇编结果的关键部分
如图2.1.2所示,下方红框中所示call 8048491 <foo>
表示该指令将调用地址在0x8048491处的foo函数,其对应的机器指令为e8 d7 ff ff ff
,其中e8
为跳转,d7 ff ff ff
为此时EIP
寄存器中值08 04 84 ba
为跳转到08 04 84 91
地址处所需要加上的值,即-41
的补码。
如果我们想让该程序在这里返回的地址变成getShell
函数的地址,只需要修改e8
之后的数值,使其等于getShell
函数地址减去EIP
寄存器中值08 04 84 ba
所得结果的补码即可。
计算过程为:
小端序就是c3 ff ff ff
,也就是说,我们只需要将e8
之后的地址改为c3 ff ff ff
即可。
- 修改实践
使用vi
打开pwn20232304
文件,结果如图所示
图2.1.3 使用vi打开pwn20232304文件的结果
使用vi中的%!xxd
命令将输出结果转为以十六进制显示,结果如下:
图2.1.4 使用十六进制显示pwn20232304文件内容
使用/d7ff
命令查找要修改的内容,结果如下
图2.1.5 查找要修改的值
之后把d7
修改成c3
,如图所示
图2.1.6 修改d7为c3
之后转换十六进制为原格式,使用%!xxd -r
命令并存盘退出,如图所示
图2.1.7 将十六进制转换为原格式并保存退出
再次使用odjump
工具对pwn20232304
文件进行反编译,以验证是否修改正确,结果如图
图2.1.8 再次通过反编译验证是否修改正确
执行修改后的文件,得到了shell提示符,证明攻击成功,结果如图所示
图2.1.9 运行结果
2.2通过构造输入参数,造成BOF攻击,改变程序执行流
-
观察程序功能
首先还是先观察程序功能,我们还是从图2.1.2所展示内容来看,
main
函数在调用foo
函数时,指令lea -0x1c(%ebp), %eax
计算缓冲区的起始地址,其中偏移量-0x1c
表示缓冲区从ebp
指针向低地址方向延伸0x1c
字节(即 28 字节)。这对应 C 代码中的类似char buffer[28];
的声明;证明这里读入字符串时,系统只预留了28字节的缓冲区,这里便可以造成缓冲区溢出,由于main
函数在调用foo时会将下一个指令的地址(即call
指令的下一行,地址为08 04 84 ba
)压入堆栈,以使foo
函数执行完之后继续执行下一行命令,所以我们只需要在输入字符串时输入超过 28字节 的字符串就有可能覆盖到返回地址,如果将返回地址覆盖成getShell
函数的地址,就能达到获取shell的效果。 -
在程序中可以看到,
foo
函数在指令lea -0x1c(%ebp), %eax
将缓冲区的地址加载到eax
,然后传递给gets
。这意味着缓冲区从ebp - 0x1c开始。缓冲区的大小取决于它如何定义。但栈帧总大小是0x38(56)字节,缓冲区是其中的一部分。从ebp - 0x1c到ebp之间是28字节,缓冲区可能只占用这部分空间。为了覆盖返回地址,我们需要知道从缓冲区开始到返回地址的偏移。返回地址保存在栈上,在ebp之后。典型栈布局:
- 调用函数后,栈上保存返回地址,然后push ebp设置新栈帧。
- 所以,在foo函数中:ebp指向保存的旧ebp。返回地址在ebp + 4。局部变量从ebp - 偏移开始。
缓冲区从ebp - 0x1c开始。到返回地址的距离是:
- 从缓冲区开始到ebp:0x1c字节(28字节)。
- 然后
ebp
本身占4字节(32位系统),所以返回地址在ebp + 4
。 - 因此,从缓冲区开始到返回地址的偏移是:0x1c + 4 = 0x20(32字节)。
实践证明:
用gdb
对程序进行调试,输入111111112222222233333333444444445555,最后使用info r
命令查看到eip
已经被5555(ASCII码为35 35 35 35
覆盖),如图所示:
图2.2.1 证明第33字节开始为返回地址
使用字符串1111111122222222333333334444444412345678进行调试,得到eip
被覆盖为34 33 32 31
,证明了该系统存储方式为小端序,如图所示
图2.2.2 证明系统存储方式为小端序
所以,我们的字符串在第33字节处开始覆盖返回地址,由于返回地址在32位系统中占4字节,所以我们只需要构建任意32字节数据+要返回的函数地址的字符串作为函数输入,即可触发getShell
函数。由之前的反编译分析我们可以知道该系统的存储方式为小端序,并且我们所需要触发的函数getShell
的地址为08 04 84 7d
,所以我们所构建的字符串为:
- 使用perl生成字符串,利用
>
重定向将字符串输出到文件input
中,并使用xxd
工具确认文件内容是否正确,如图所示:
图2.2.3 生成字符串输出到文件与文件内容确认
-
将
input
文件内容作为程序输入,运行程序使用
cat
命令读取文件内容,并使用|
管道符将输出作为程序输入,最后得到的结果如图所示,成功得到了shell,如图所示
图2.2.4 成功得到shell
2.3 注入Shellcode并执行
-
准备工作
首先设置堆栈可执行,由于使用的环境为新版Kali,已不再支持
execstack
,于是使用相同功能的工具patchelf
,如图所示:
图2.3.1 使用patchelf设置堆栈可执行
之后关闭地址随机化,注意,这里必须使用root用户才能执行操作,管理员用户使用sudo
不可以执行,如图所示:
图2.3.2 关闭地址随机化
-
确定Shellcode最后四位地址
使用一个终端窗口注入目前最后四位地址未知暂设为
1234
的Shellcode,另开一个终端窗口对该程序进行调试,如图所示:
图2.3.3 第一次注入与调试过程
首先在另一个终端找到pwn1程序的进程号,使用gdb对该程序进行调试,由上图可知进程号为87363,调试过程如图所示
图2.3.4 调试过程
之后对foo
函数设置断点,以查看注入buf的内存地址,如图所示
图2.3.5 添加断点
看到断在0x80484ae,此时在另一个终端按下回车,如图所示
图2.3.6 继续调试
在0xffffcf8c
处找到了1234(0x01020304)
,于是从0xffffcf90
开始就是我们的Shellcode.(使用anything+retaddr+nops+shellcode结构),使用perl构建Shellcode,如图所示
图2.3.7 构建Shellcode
注入运行,成功获取shell,如图所示
图2.3.8 运行结果
在同一台主机上结合nc实现远程模拟攻击,如图所示
图2.3.9 远程模拟攻击
3.问题及解决方案
-
问题1:没有execstack?
-
问题1解决方案:
询问AI得到答案:execstack已不存在于新版Kali上,可以使用patchelf代替
-
问题2:Shellcode部分直接按照实验指导书的命令使用不成功?
-
问题2解决方案:
细看才发现每个人的返回地址是不一样的,需要根据自己的地址确定合适的Shellcode
4.学习感悟、思考等
通过本次实验,我深刻体会到网络攻防技术是理论与实践紧密结合的领域,亲手实践缓冲区溢出的三种攻击方式让我对系统底层机制如汇编指令、内存布局和函数调用栈有了直观认识,理解了缓冲区溢出漏洞的巨大危害性以及安全编程和系统防护的重要性;在反复调试和排除故障的过程中,我不仅提升了使用反汇编工具、调试器和十六进制编辑器等底层分析和操作能力,更培养了面对技术挑战时冷静分析、严谨求证的科学态度,认识到细致耐心在攻防实践中的关键作用,这些收获对我未来深入网络安全领域的学习和研究奠定了坚实基础。
参考资料
- 0x11_MAL_逆向与Bof基础.md