20252918 2024-2025-2 《网络攻防实践》实践9报告
1.实践内容
本次实验以Linux可执行文件pwn1作为分析目标。在程序的正常执行流程中,main函数会调用foo函数,而foo函数仅负责将用户输入的字符串原样回显输出。此外,程序中还包含一个未被任何函数调用的getShell函数,该函数能够返回一个交互式Shell。
本次实验的核心任务是通过三种不同的技术手段,分别劫持程序的执行流程,使其转而执行getShell函数并成功获得Shell。通过这些实践,深入理解缓冲区溢出漏洞的形成原理及其典型利用方式。
共三个核心实践任务:
任务一:手工修改可执行文件,改变程序执行流程
直接编辑目标程序的二进制机器码,修改CALL指令的操作数,使main函数在调用时转向getShell函数,从而实现程序执行流的静态劫持。
任务二:利用foo函数的缓冲区溢出漏洞,触发getShell
分析foo函数中gets函数导致的缓冲区溢出漏洞,精心构造恶意输入字符串,覆盖栈中保存的函数返回地址,使程序在foo函数执行ret指令时跳转到getShell函数执行。
任务三:注入自定义Shellcode并执行
构造一段可独立执行的Shellcode,借助缓冲区溢出漏洞将其注入目标程序的内存空间,同时劫持程序执行流使其运行该Shellcode,从而不依赖程序内建getShell函数的情况下完成Shell获取。
实验要求
- 掌握NOP、JNE、JE、JMP、CMP、CALL、RET等核心汇编指令对应的机器码
- 熟练使用反汇编工具与十六进制编辑器,完成二进制文件的分析与修改
- 能够通过修改机器指令,精准改变程序的正常执行流程
- 理解缓冲区溢出攻击的核心原理,能够独立构造攻击载荷(Payload)完成BoF攻击
相关理论知识
缓冲区溢出(Buffer Overflow)
缓冲区溢出是一种高危软件安全漏洞,其本质在于程序向内存缓冲区写入数据时,超出了预先分配的内存边界,导致多余的数据覆盖了相邻的内存区域。攻击者可借此覆盖函数返回地址、函数指针等关键控制信息,从而劫持程序执行流,执行恶意代码。
BoF攻击原理
BoF攻击的核心在于利用不具备边界检查功能的输入函数(如本实验中的gets函数),向缓冲区写入超长数据以覆盖栈帧中的函数返回地址。当被攻击函数执行ret指令时,CPU会将已被覆盖的恶意地址加载到指令指针寄存器(EIP)中,从而跳转到攻击者指定的地址执行代码。
一个完整的BoF攻击Payload通常由以下几个部分构成:
- 填充数据:填满缓冲区与栈帧保留空间
- 恶意返回地址:覆盖原始函数返回地址
- Shellcode:攻击者想要执行的恶意代码
在某些场景中,攻击者还会在Shellcode前添加NOP空指令滑板,以提高攻击成功率。
Shellcode
Shellcode是一段可直接被CPU执行的机器码,因通常用于获取目标系统的交互式Shell而得名。Shellcode需针对目标CPU架构编写(本实验为x86 32位架构),并满足体积精简、无坏字符等要求,以确保能够在漏洞利用场景中正常执行。
2.实践过程
任务一:
用objdump进行反汇编:

main函数占据的内存地址范围是从0x080484af到0x080484c0,其中在地址0x080484b5处存放了一条调用foo函数的call指令,对应的机器码是e8 d7 ff ff ff。这条指令的长度为5个字节,因此它的下一条指令起始地址为0x080484ba。foo函数的起始地址是0x08048491,该函数内部只调用了gets和puts函数,用于读取用户输入并原样回显,但gets函数没有对输入长度做任何检查。程序中还有一个未被调用的getShell函数,其起始地址为0x0804847d,该函数内部通过调用system函数来执行/bin/sh,从而返回一个交互式Shell。
CALL指令偏移量计算:
在x86架构下,CALL指令对应的机器码格式为0xE8,后面紧跟一个4字节的相对偏移量。这个偏移量的计算方式是用目标函数的地址减去CALL指令下一条指令的地址。原始程序中,从main函数调用foo时,目标地址为0x08048491,CALL指令的下一条指令地址是0x080484ba,两者相减得到0xffffffd7,以补码形式表示负数,按小端序存储后即为d7 ff ff ff,这与反汇编结果完全一致。若希望程序转而调用getShell函数,则需要用getShell的起始地址0x0804847d减去同一个下一条指令地址0x080484ba,计算结果为0xffffffc3,小端序表示为c3 ff ff ff。因此,只需将原CALL指令机器码中的d7 ff ff ff替换为c3 ff ff ff,就可以使main函数在运行时跳转到getShell而不是foo,从而实现执行流程的静态劫持。
二进制文件的十六进制编辑:

定位/e8 d7:

修改为c3:

查看是否成功:

运行验证:

任务二:
生成Payload:

执行BoF:

成功。
任务三:
execstack安装:由于在线安装失败,只能现在windows下好之后,移动过去。

关闭堆栈保护机制,允许直接在栈上执行:
输出包含X就是成功:

关闭ASLR:
输出0就是成功:

生成Payload,并在第一个终端加载Payload并保持运行:

查找pwn1的进程PID:

GDB附加调试进程:


成功。
3.学习中遇到的问题及解决
- 问题1:execstack在线安装失败
- 问题1解决方案:网站下载:http://archive.ubuntu.com/ubuntu/pool/universe/p/prelink/
4.实践总结
本次实验通过三种不同的技术手段,完整实现了对Linux 32位程序的缓冲区溢出攻击。从静态修改二进制文件以劫持执行流,到动态利用BoF漏洞触发程序内置函数,再到注入并执行自定义Shellcode,实验设计层层递进,帮助我深入理解了缓冲区溢出漏洞的底层原理与典型利用方式。
在实验过程中,我不仅掌握了objdump、gdb、xxd等常用二进制分析工具的使用方法,还进一步理解了x86架构下程序的栈帧布局、函数调用与返回的底层机制,以及CALL、RET等汇编指令对程序执行流程的控制逻辑。实验中遇到的各类权限问题和程序崩溃现象,也极大锻炼了我排查和解决问题的能力,使我认识到漏洞利用对精确性的高度依赖——哪怕只有一个字节的偏移错误,也会导致攻击失败。
