abdullah-5j/stack-buffer-overflow-exploit
GitHub: abdullah-5j/stack-buffer-overflow-exploit
一个教学性质的 C++ 栈缓冲区溢出漏洞利用演示项目,完整展示了从漏洞分析到 shellcode 注入的利用全流程。
Stars: 1 | Forks: 0
# 栈缓冲区溢出 — Shellcode 注入




## 📖 概述
缓冲区溢出是软件安全中最古老且最具教育意义的漏洞类型之一。本项目完整演示了其整个生命周期:
1. **漏洞** — 一个 C++ 程序在读取用户输入到固定大小的栈缓冲区时,没有进行边界检查。
2. **分析** — 使用 **Immunity Debugger** 检查栈,找到覆盖已保存返回地址的精确偏移量,并定位可用的 `JMP ESP` 指令。
3. **利用** — 构造 payload(填充 + 返回地址覆盖 + NOP sled + shellcode),将执行流重定向到我们自己的代码。
4. **结果** — 漏洞程序启动 `calc.exe`,而不是正常返回。
## 🧩 漏洞
```
char buffer[64];
cout << "Enter your name: ";
gets(buffer); // <-- no bounds checking
```
根本原因在于使用了 **`gets()`**,这是一个非常危险的函数,甚至被 **从 C11 标准中移除**。它会一直读取输入直到遇到换行符,且*完全无法限制长度*,因此任何超过 64 字节的输入都会越过 `buffer` 的末尾,破坏相邻的栈内存 —— 包括 CPU 用于决定 `main()` 结束后跳转位置的**已保存返回地址**(EIP)。
通过控制该返回地址,攻击者就可以控制程序接下来执行的位置。
完整的技术分析请参阅 [`docs/VULNERABILITY.md`](docs/VULNERABILITY.md)。
## 🛠️ 漏洞利用原理
由 [`src/exploit.cpp`](src/exploit.cpp) 编写的 payload 布局如下:
```
[ 76 bytes 'A' padding ][ JMP ESP address ][ NOP sled ][ shellcode ]
fills buffer + overwrites the lands spawns
saved EBP return address execution calc.exe
```
| 组件 | 大小 | 作用 |
|-----------|------|------|
| 填充 (`AAAA...`) | 76 字节 | 填充缓冲区直至已保存的返回地址 |
| `JMP ESP` 地址 | 4 字节 | 覆盖 EIP;指向跳转到栈的指令 |
| NOP sled (`0x90`) | 16 字节 | 为 CPU 提供一条安全的“跑道”,以滑入 shellcode |
| Shellcode | ~118 字节 | 解析 `WinExec` 并运行 `calc.exe` |
偏移量(**76**)和 `JMP ESP` 地址都是**在 Immunity Debugger 中手动找到的** —— 这个发现过程是本项目真正的学习核心,该发现过程也是本项目的核心学习内容。
## 🧪 复现步骤(实验环境配置)
1. 使用旧版/宽松的工具链(32 位)**编译漏洞程序**。
2. 为研究**禁用保护机制**(这正是该漏洞利用能够成功的原因)
- **DEP**(数据执行保护)
- **ASLR**(地址空间布局随机化)
- 栈 Canary / `/GS`(编译器栈保护)
3. **生成 payload:** 编译并运行 [`src/exploit.cpp`](src/exploit.cpp) 以生成 `payload.bin`。
4. 将 **Immunity Debugger 附加**到漏洞进程并向其输入 payload。
5. 观察执行流跳入 shellcode → **计算器打开**。
## 🛡️ 仅在关闭保护机制时有效
一个关键的学习成果:这个漏洞利用之所以成功,是因为我们**关闭了防御机制**。在正常配置的现代系统上,以下任何一项都会破坏它:
| 缓解措施 | 作用 | 对该漏洞利用的影响 |
|------------|--------------|------------------------|
| **DEP / NX** | 将栈标记为不可执行 | 栈上的 shellcode 将无法运行 |
| **ASLR** | 随机化内存地址 | 硬编码的 `JMP ESP` 地址变得不可靠 |
| **栈 Canary (`/GS`)** | 在返回地址前放置保护值 | 在 `main()` 返回前检测到溢出 |
| **安全函数** | `fgets()` / `gets_s()` 强制执行长度限制 | 溢出根本不会发生 |
**源代码中的修复只需一行:**
```
// Vulnerable:
gets(buffer);
// Safe:
fgets(buffer, sizeof(buffer), stdin);
```
## 📂 仓库结构
```
.
├── README.md
├── src/
│ ├── vulnerable.cpp # The program with the buffer overflow
│ └── exploit.cpp # Builds payload.bin (padding + JMP ESP + NOP + shellcode)
├── docs/
│ ├── VULNERABILITY.md # Deep-dive: why gets() breaks, stack anatomy
│ └── LEARNINGS.md # What we learned
└── LICENSE
```
## 🎓 学习心得
它将 C++ 语言基础与实际的安全后果联系了起来。重点包括:
- **调用栈**的实际工作原理 —— 缓冲区、已保存的 EBP 和返回地址。
- 为什么像 `gets()` 这种**内存不安全函数**很危险,以及有哪些安全的替代方案。
- **小端序**字节排序(为什么 `JMP ESP` 地址是倒序写入的)。
- 在实时调试器中读取**汇编和 CPU 寄存器**(ESP、EIP)。
- 现实世界中**漏洞利用缓解措施**的目的 —— 并深刻认识到它们*为什么*存在。
完整的心得体会请见 [`docs/LEARNINGS.md`](docs/LEARNINGS.md)。
## 📄 许可证
出于教育目的,在 [MIT 许可证](LICENSE)下发布。
标签:Shellcode, x86汇编, 安全, 技术调研, 缓冲区溢出, 超时处理, 逆向分析