pwn漏洞利用手法汇总

1. 栈溢出(Stack Overflow)
简单的 ret 控制流
利用函数
gets()
/strcpy()
等读入超过缓冲区大小的数据,覆盖保存的RIP
。Payload 结构:
1
[ padding to saved RBP ] [ saved RBP overwrite ] [ new RIP ]
示例:
1
2
3
4
5
6void vuln() {
char buf[64];
gets(buf);
}
// 溢出 64 字节 + 8 字节 RBP,然后覆盖 RIP
payload = b"A"*72 + p64(0x400570);
ROP (Return-Oriented Programming)
目的:在开启 NX (DEP)时,无法执行栈上 shellcode,只能“串”现有代码片段(Gadget)形成链。
Gadget 搜集:
1
2ROPgadget --binary target_binary --only "pop|ret"
rp++ target_binary链构造:
1
2
3
4
5rop = ROP(elffile)
rop.raw(b"A"*offset)
rop.call(elffile.plt['puts'], [elf.got['puts']])
rop.call(elffile.symbols['main'])
payload = rop.chain()
ret2libc
- 思路:通过泄漏 libc 中函数地址,算出
system()
、"/bin/sh"
在内存中的偏移,再覆盖RIP
跳到system
。 - 流程:
- ROP 调用
puts(puts@got)
→ 泄漏 libc 基址。 - 计算
system = libc_base + offset_system
,binsh = libc_base + offset_str_sh
。 - 再次溢出,ROP →
system(binsh)
。
- ROP 调用
- 思路:通过泄漏 libc 中函数地址,算出
ret2dlresolve
用途:当 binary 没有
system
或你想不修改 GOT,只能借助动态链接器_dl_runtime_resolve
构造符号解析。核心:构造假的
.rel.plt
/.dynsym
/.dynstr
结构体片段,ROP 跳到_dl_runtime_resolve
,让它解析并调用任意函数。代码片段(pwntools 自动化):
1
2
3
4
5
6
7from pwn import *
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
rop = ROP(elf)
rop.raw(b"A"*offset)
rop.call(elf.symbols['read'], [0, dlresolve.data_addr, len(dlresolve.data)])
rop.call(dlresolve.resolve_addr)
payload = rop.chain() + dlresolve.data
2. 格式化字符串(Format String)
%p 和 %n 原理
%p
:打印栈上指针,泄漏地址。%n
:将到目前写入的字节数写入对应参数指向的地址,可任意写。
任意读写
泄漏阶段:
%7$p
,%9$p
等确定栈上偏移。写入阶段(任意地址写一个字节或两字节):
1
printf("%[num]c%[offset]$hhn", value, addr_ptr);
GOT 重写
- 利用
%n
将 GOT 中的函数入口(如exit@got
)重写为system@plt
→ 执行system("/bin/sh")
。
- 利用
复杂 payload 拼接
- 分多次写不同字节,每次用精准的
%c
调整数值。 - 常用工具:
fmtstr_payload
(pwntools)自动化生成写任意 64 位地址的字符串。
- 分多次写不同字节,每次用精准的
3. 堆漏洞(Heap Exploitation)
- glibc malloc 内部结构
- Fastbins:大小 ≤ 0x80,LIFO 快速分配。
- Tcache(glibc 2.26+):每个 size-class 最多缓存 7 个 chunk。
- Unsorted Bin、Small Bin、Large Bin:更大或过期的 chunk 流转。
- 常见利用手法
- tcache poisoning
free(A); free(B); free(A);
→ A 双重入 tcache。- 修改 A 的
fd
指向目标地址X
。 malloc(A); malloc(A); malloc(A);
→ 第三次返回X
,可写任意位置。
- House of Force
- 利用
top chunk
大小无限,调整brk
到任意位置再malloc
得到任意地址。
- 利用
- House of Spirit
- 在数据区伪造一个可用 chunk header,把它的地址当作 free chunk,
free(fake_ptr)
→ 往任何地址写入到 tcache。
- 在数据区伪造一个可用 chunk header,把它的地址当作 free chunk,
- 其他 “House of …”
- House of Lore:用 small bin 的伪造控制,写任意两字位置数据。
- House of Orange:在 unsorted bin 用 io_list_all 伪造,触发 FILE vtable 利用。
- House of Einherjar:结合 tcache, fastbin, unsorted bin 等混合利用。
- off-by-one / overlap chunk
- 越界 1 字节改 size 字段,伪装相邻 chunk 大小,造成合并后可写。
- double free
- 未正确检测重复 free,导致 tcache 或 fastbin 污染。
- use-after-free
- free 后继续使用指针,配合覆盖 chunk header 改变下次分配。
- tcache poisoning
4. 文件流 & FILE 结构利用(_IO_FILE Exploitation)
核心:glibc 的
FILE
结构(_IO_FILE_plus)有 vtable 和函数指针,可伪造_IO_list_all
链表或 vtable 指针,触发垂死攻击。典型流程:
泄漏 libc 地址后,计算
_IO_list_all
、_IO_str_overflow
、system
等偏移。在堆上伪造一个
_IO_FILE
结构:1
2
3
4
5
6
7
8
9
10struct _IO_FILE fake = {
._flags = 0,
._IO_read_ptr = 0,
._IO_write_base = 1, // non-zero
._IO_write_ptr = 0,
.vtable_offset = 0,
._IO_buf_base = (char*)system,
._IO_buf_end = "/bin/sh",
.vtable = _IO_str_jumps + 0x20 // 指向 _IO_overflow
};将这个 fake 文件结构地址写入
_IO_list_all
。调用
exit()
或abort()
时,glibc 会遍历_IO_list_all
,执行 vtable 指针,跳转到system("/bin/sh")
。
5. 信号 & SROP(Sigreturn-Oriented Programming)
原理:利用
sigreturn
系统调用,恢复自定义的 ucontext 结构,从而修改寄存器(包括RIP
、RSP
、RAX
)并进行 syscalls。步骤:
- 准备:溢出控制
RSP
,让栈指向你构造的 fakesigframe
。 - 触发 sigreturn:构造 ROP,执行
syscall; ret
,此时RAX=15
(__NR_sigreturn),CPU 会将栈上的 fake frame 恢复到上下文。 - 执行自定义 syscalls:在 fake frame 中设置
RAX=59(execve)
,RDI=ptr("/bin/sh")
,RSI=0
,RDX=0
,并让RIP
指向syscall
,即可获得 shell。
- 准备:溢出控制
示例(pwntools 自动):
1
2
3
4
5
6frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
payload = b"A"*offset + p64(syscall_ret) + bytes(frame)
- Title: pwn漏洞利用手法汇总
- Author: luyanpei
- Created at : 2025-05-09 18:07:05
- Updated at : 2025-05-09 18:49:36
- Link: https://redefine.ohevan.com/2025/05/09/漏洞利用手法汇总/
- License: All Rights Reserved © luyanpei