从零到一:CTF PWN实战中ret2text漏洞的深度解析与自动化利用
如果你刚开始接触CTF中的PWN方向,面对一堆反汇编代码和内存地址感到无从下手,那么这篇文章就是为你准备的。我不会给你堆砌晦涩的理论,而是带你亲手完成一次真实的漏洞利用,从分析到拿到shell,让你真正理解栈溢出攻击的核心逻辑。今天我们要攻克的是一道经典的ret2text题目,这类题目在CTFHub等平台上很常见,也是PWN入门的必经之路。
很多新手在第一次尝试时,往往卡在偏移计算或payload构造上,调试半天也拿不到flag。其实关键在于理解程序的内存布局和函数调用机制。ret2text之所以被称为“最简单”的栈溢出,是因为程序本身就包含了我们需要的“后门代码”——通常是system("/bin/sh")这样的函数调用。我们的任务就是找到它,然后让程序跳转到那里执行。
1. 环境准备与初步分析
在开始之前,你需要准备一个合适的实验环境。我推荐使用Kali Linux或者Ubuntu系统,因为它们预装了很多安全工具。如果你用Windows,可以通过WSL2或者虚拟机来搭建环境。
1.1 必要工具安装
首先确保你安装了以下工具:
# 更新包管理器
sudo apt update
# 安装Python3和pip
sudo apt install python3 python3-pip
# 安装pwntools - 这是我们的主力武器
pip3 install pwntools
# 安装反汇编工具
sudo apt install gdb gdb-multiarch
# 安装IDA的替代品(免费) - radare2
sudo apt install radare2
# 检查工具是否安装成功
python3 -c "import pwn; print('pwntools版本:', pwn.__version__)"
提示:如果你在使用pwntools时遇到编码问题,可以在Python脚本开头添加
# -*- coding: utf-8 -*-,或者使用context(encoding='latin-1')来设置编码。
1.2 题目文件获取与分析
从CTFHub下载题目附件后,我们首先要用file命令查看文件类型:
$ file pwn
pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0,
BuildID[sha1]=9dc32140f0e317f9e6a59b9a226a5123e34ace21, not stripped
关键信息一目了然:64位ELF文件,动态链接,没有去除符号表(not stripped)。这意味着函数名和变量名都还在,分析起来会容易很多。
接下来用checksec检查程序的安全机制:
$ checksec --file=pwn
[*] '/home/kali/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
这个结果非常友好——所有常见的安全保护都没开启。没有栈金丝雀(Canary),意味着我们可以随意溢出;没有NX(不可执行栈),我们甚至可以在栈上执行代码;没有PIE(地址空间随机化),所有函数的地址都是固定的。这简直就是为初学者准备的完美靶场。
2. 静态分析:寻找漏洞点和后门
静态分析就是在不运行程序的情况下分析它的代码逻辑。我们主要使用IDA Pro或者免费的替代品如Ghidra、radare2。这里我用radare2演示,因为它完全免费且功能强大。
2.1 主函数分析
首先用radare2打开程序:
r2 -A pwn
然后查看主函数的反汇编:
[0x00400610]> pdf @ main
你会看到类似下面的代码:
int main(void) {
char buffer[112];
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
puts("Welcome to CTFHub ret2text challenge!");
gets(buffer); // 危险函数!
puts("Goodbye!");
return 0;
}
看到gets(buffer)了吗?这就是漏洞所在。gets()函数从不检查输入长度,它会一直读取直到遇到换行符或EOF。如果我们的输入超过112字节,就会覆盖栈上的其他数据。
2.2 寻找后门函数
在ret2text中,关键是要找到程序自带的“后门”。用radare2搜索字符串:
[0x00400610]> iz
[Strings]
Num Paddr Vaddr Len Size Section Type String
000 0x000008cb 0x004008cb 8 9 .rodata ascii /bin/sh
找到了!/bin/sh字符串在地址0x4008cb。现在查找哪个函数使用了这个字符串:
[0x00400610]> axt 0x4008cb
data 0x4007b8 in main
查看这个地址附近的代码:
[0x00400610]> pdf @ 0x4007b8
你会看到类似这样的代码:
0x004007b8: lea rdi, [rip + 0x10c] ; 加载"/bin/sh"到rdi
0x004007bf: call sym.imp.system ; 调用system函数
0x004007c4: nop
这就是我们的目标——system("/bin/sh")的调用点,地址是0x4007b8。在64位Linux中,rdi寄存器存放第一个参数,所以这里把/bin/sh的地址加载到rdi,然后调用system。
3. 计算偏移量:精确控制程序流
要成功利用漏洞,我们需要知道从输入缓冲区开始到返回地址之间有多少字节。这就像知道要填充多长的"垃圾数据"才能刚好覆盖到关键位置。

&spm=1001.2101.3001.5002&articleId=153246372&d=1&t=3&u=7b3d68ad7d3247059cffa57cf9361813)
39

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



