赛题链接
https://github.com/eternalsakura/ctf_pwn/tree/master/湖湘杯2017/pwn100h
测试
先随手输入测试一下
从结果来看很像栈溢出了。
静态分析找到漏洞
打开IDA查看代码
找到main函数,跟进sub_8048B29()
跟进去找到输入点,可以看到最大可以输入512字节的数据,对于输入格式的要求是能够进行base64解码。
根据我圈起来的地方基本上可以很明显的看出这是base64解密了
如果对base64还有不懂,可以参考base64 c语言实现和维基百科
找到栈溢出
base64解码的结果存入char数组v21[257],base64解码之后的数据大小大概是原来的3/4,足够造成栈溢出了。
漏洞函数地址为0x080487E6
分析下多进程
wait(0)函数的作用是等待任意一个子进程退出。
http://www.cnblogs.com/linux-sir/archive/2012/01/27/2330014.html
利用漏洞
checksec查看开启了什么保护,这题的难点就在canary了。
利用思路
一般的思路是先leak出canary的cookie,然后在payload里,把原来的canary位置的cookie用我们leak出的正确的cookie写入,之后就是正常的rop。
不过这题,emmm,有个fork呀……参考这篇文章http://0x48.pw/2017/03/14/0x2d/,直接爆破canary
这是个32位的程序,所以canary有4个字节,最低位一定是\x00,所以只需要爆破三个字节即可。
构造爆破payload格式为:padding+canary+chr(i)
(canary的玩法不要太多,可以参考这篇文章:http://veritas501.space/2017/04/28/论canary的几种玩法/
爆破
先找到padding的大小
再看看canary在哪,在数组旁边。
所以我们的padding大小就是257字节了。
再重复一遍,对fork而言,作用相当于自我复制,每一次复制出来的程序,内存布局都是一样的,当然canary值也一样。 那我们就可以逐位爆破,如果程序GG了就说明这一位不对,如果程序正常就可以接着跑下一位,直到跑出正确的canary。
爆破函数
1 | canary = '\x00' |
构造rop
关于构造rop可以参考蒸米的文章:https://yq.aliyun.com/articles/58699
leak出puts函数的地址
所以我们的payload1格式就是rop = padding + canary + padding + puts_plt_addr + 漏洞函数地址 + puts_got_addr(作为puts的参数)
计算system地址和在libc里找到’/bin/sh’地址
libc.symbols['system'] - libc.symbols['puts'] + u32(puts_addr)
next(libc.search('/bin/sh')- libc.symbols['puts'] + u32(puts_addr)
return to system,getshell
payload2格式为rop=padding+canary+padding+system_addr+padding+sh_addr
exp
1 | from pwn import * |