题目链接
https://github.com/eternalsakura/ctf_pwn/blob/master/pwnme_k0
分析
这个题代码写的略智障。
其实一共是从rbp-30到rbp-8这么一段空间,一共40个字节来存来存账号和密码。
但是存账号是从v16到char v18[20]的前4个字节,确实是16个,但是这么写不会很怪么……
密码也是,从数组的第4个字节之后开始存,最大存20个字节,一开始看还以为有溢出,碎碎念一下。
不过实际上漏洞是在打印个人信息的时候,能看到一个格式化字符串漏洞。
而这个&a9+4实际上就是我们输入的密码。
利用
checksec
1 | sakura@ubuntu:~$ checksec pwnme_k0 |
vulfunc
这题看了下字符串,发现有可以直接利用的system(‘/bin/sh’),所以只要用格式化字符串漏洞直接修改某个函数的返回地址为0x4008A6就可以了。
确定偏移
首先来跟随一下程序,确定格式化串的相对偏移。
写一个程序确定偏移。
1 | from pwn import * |
输出
1 | sakura@ubuntu:~$ python offset.py |
下面si只是为了跟进printf函数,个人习惯……不跟也行,只要你知道怎么数格式化串在哪,跟进去的话栈上第一个就肯定是返回地址,注意这里是64位程序,所以返回地址后跟着的就是参数7(offset 6),参数8(offset 7)…..
1 | pwndbg> si |
这样就找到了,偏移为10,不过0x7025702500000000被00截断了,应该用“后入式“,把地址写在后面,所以偏移应该取12
修改返回地址
我们知道:虽然存储返回地址的内存本身是动态变化的,但是其相对于rbp的地址并不会改变,所以我们可以使用相对地址来计算。
1 | [────────────────────────────────────STACK─────────────────────────────────────] |
这里的返回地址是printf的返回地址,此时rbp还没有变化,还没有进入printf,还是当前函数的rbp,则rbp指向的就是old rbp的地址。
所以当前的返回地址就在rbp+8,即0x400d74。
存储返回地址的内存就是0x7fff8ee7f1c8,它相对于相对于old rbp的地址就是:0x7fff8ee7f200-0x7fff8ee7f1c8=0x38。
(这部分说的有点乱,先这么理解着吧……)
总之用格式化串先读0x7fff8ee7f1c0地址(offset 6),得到rbp的地址是0x7fff8ee7f200,再减去0x38就得到存储返回地址的内存地址是0x7fff8ee7f1c8。
然后leak出这个地址后,就可以去覆盖这个地址存放的返回值为我们的system(‘/bin/sh’)即0x4008A6
当函数返回的时候就getshell.
1 | from pwn import * |
getshell
这题的username我完全没用到,不过其实结合username更好用一些,不过
为了练习”后入式“,我就写的麻烦一点。
exp如下:
1 | from pwn import * |
其他
在写payload的时候
我一直把%2214u%12$hn数成11……然后一直GG。
1 | sakura@ubuntu:~$ python -c 'print len("%2214u%12$hn")' |
之所以做这道题。。是因为百度杯11月的一道pwn题和这题几乎一模一样…就拿来折腾下好了,不过那题没有system(‘/bin/sh’)可以利用。
要考虑leak出来。
格式化字符串大部分的利用姿势我都练到了,不过还是不够熟练,慢慢来呗~