【CTF】日志 2019.7.13 pwn 堆溢出基础知识
十六进制两位表示一个字节
堆溢出
先上堆图:

堆的数据结构
一般情况下,物理相邻的两个空闲 chunk 会被合并为一个 chunk
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /*上一个chunk空闲时记录上一个chunk的大小,上一个chunk被使用时作为上个chunk的数据部分 */
INTERNAL_SIZE_T size; /* 本chunk的大小,低三位是标志位*/
struct malloc_chunk* fd; /* 本chunk空闲时指向下一空闲的chunk,被使用时作为数据部分的指针 */
struct malloc_chunk* bk; /* 本chunk空闲时指向上一空闲的chunk,被使用时作为数据部分 */
/* 仅在大chunk中使用,指向的都是大的chunk ,用于对大的chunk按照大小排序,以减少遍历chunk的开销*/
struct malloc_chunk* fd_nextsize; /* 指向上一个与本chunk不同大小的chunk */
struct malloc_chunk* bk_nextsize; /* 指向下一个与本chunk不同大小的chunk */
};
size的低三位标志位从高到低是:
A:NON_MAIN_ARENA,是否不属于主线程,1 表示不属于,0 表示属于。
M:IS_MAPPED,是否由 mmap 分配
P:PREV_INUSE,是否被分配
bin
会根据空闲的 chunk 的大小以及使用状态将 chunk 初步分为 4 类:fast bins,small bins,large bins,unsorted bin
其中 unsorted bin, small bins,large bins 依次被组织到一个数组中,数组下标表示的是chunk上的fd或者bk指针,并不代表第几个bin。每一个bin中都是一个链表,包含着多个chunk
1 unsorted bin
没有排序的bin
来源:
1)当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
2)释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。
2 small bins
数组下标 2 - 63 是small bin,每一个bin下的链表存储着相同大小的chunk,每两个相邻的bin代表的大小为 2个机器字长
small bins 中每个 chunk 的大小与其所在的 bin 的 index 的关系为:chunk_size = 2 * SIZE_SZ *index,具体如下
| 下标 | SIZE_SZ=4(32 位)| SIZE_SZ=8(64 位)|
|--|--|--|--|
| 2 | 16 | 32 |
|3 | 24 | 48|
|4 |32 | 64|
|5 |40 |80|
|x | 2*4*x | 2*8*x|
|63 | 504 | 1008 |
分配采用 FIFO 的规则
3 large bin
small bin之后是large bin,一共有63个bin,每个bin代表一定范围内的chunk,相同大小的按照最近使用排列
以 32 位平台的 large bin 为例,第一个 large bin 的起始 chunk 大小为 512 字节,位于第一组,所以该 bin 可以存储的 chunk 的大小范围为 [512,512+64)。
| 组 | 数量 | 公差 |
|---|---|---|
| 1 | 32 | 64B |
| 2 | 16 | 512B |
| 3 | 8 | 4096B |
| 4 | 4 | 32768B |
| 5 | 2 | 262144B |
| 6 | 1 | 不限制 |
4 fast bin
当释放的空间大小小于MAX_FAST_SIZE时,为了节省下一次的遍历开销,会将这部分的空间存放到fastbin中,这样这些经常用到的小块就不用花时间在合并和分割上。(fastbin 范围的 chunk 的 inuse 始终被置为 1。因此它们不会和其它被释放的 chunk 合并。)
分配采用LIFO后进先出的规则
fastbin 最多可以支持的 bin 的个数为 10 个
/* The maximum fastbin request size we support */
#define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
默认情况下(32 位系统为例), fastbin 中默认支持最大的 chunk 的数据空间大小为 64 字节
top chunk
地址最高的chunk
当所有的bin都无法满足时,若 top chunk 大于申请的空间,则分割,分割后剩余的部分作为新的 top chunk。
初始情况下,我们可以将 unsorted chunk 作为 top chunk。
名词:
chunk:块
arena:操作系统分配给程序的一块较大的(大于所申请的)内存区域
last remainder:在用户使用 malloc 请求分配内存时,ptmalloc2 找到的 chunk 可能并不和申请的内存大小一致,这时候就将分割之后的剩余部分称之为 last remainder chunk ,unsort bin 也会存这一块。top chunk 分割剩下的部分不会作为 last remainer.
堆操作:
- malloc(size_t n)
返回对应大小字节的内存块的指针
当 n=0 时,返回当前系统允许的堆的最小内存块
当 n 为负数时,由于在大多数系统上,size_t 是无符号数(这一点非常重要),所以程序就会申请很大的内存空间,但通常来说都会失败,因为系统没有那么多的内存可以分配
使用brk申请的堆空间会挨着数据段,而使用mmap的堆空间在较高的地址
这些内存在释放之后一般不会返回系统而是在程序中等待申请
malloc()会使用两种系统调用:
1)brk()或者sbrk()函数
操作系统提供了 brk 函数,glibc 库提供了 sbrk 函数,sbrk会调用brk
初始时,堆的起始地址 start_brk 以及堆的当前末尾 brk 指向同一地址
brk(address) 直接将program break修改到address,返回值 0成功 -1 失败
sbrk(n) 修改program break向高地址增长n个字节,返回开辟之前的program break地址
sbrk(0)返回当前program break的地址
但是program break的增长不是无限制的,天花板是Memory Mapping Segment
2)mmap()
当申请的空间大于128k时,malloc()调用mmap()
- free(void* p)
会释放由 p 所指向的内存块
当 p 为空指针时,函数不执行任何操作
当 p 已经被释放之后,再次释放会出现乱七八糟的效果,这其实就是double free
除了被禁用 (mallopt) 的情况下,当释放很大的内存空间时,程序会将这些内存空间还给系统,以便于减小程序所使用的内存空间
申请空间
malloc --> __libc_malloc --> _int_malloc
__libc_malloc(size)
用户申请的字节一旦进入申请内存函数中就变成了 无符号整数。
寻找钩子hook ----》 寻找arena ----》 调用_int_malloc分配内存 -+--》成功,返回内存
↑ |
| ↓
+-----分配失败,再寻找一个arena
_int_malloc()
--------------------------------------------------------------------------------
将size转化为对应的chunk大小 ----》 fastbin ----》 遍历(后进先出),检查大小是否符合 ----》 符合则计算索引 ----》 chunk转换为内存返回
根据大小选择bin ----》 smallbin ----》获取索引、指针 ----》 检查该bin是否为空 ----》 不为空 ----》将链表中最后一个chunk分配(先进先出)
| +----》 初始化
+---》 该bin为空
----》 不在fastbin和smallbin中 ----》 malloc_consolidate():处理fastbin ----》 可以合并的合并,然后放 unsorted bin ----》大循环
----------------------------------------------------------------------------------
大循环 ----》 遍历unsorted bin ----》 FIFO寻找大小刚好合适的bin ----》若有,bin转为内存后返回
循环10000次 ----》若没有,则将当前的unsorted bin按照大小放至对应的small或large中
----》 遍历large bin ----》对应的 bin 中从小(链表尾部)到大(头部)进行扫描 ----》 找到第一个合适的返回
----》 若大小合适的bin都不存在,则在map中找更大的bin遍历 ----》 找到,返回内存
----》 找不到,使用top chunk ----》 满足,分割后返回
----》 不满足,使用 sysmalloc 来申请内存
------------------------------------------------------------------------------------
//从 fastbin 的头结点开始取 chunk(LIFO)
sysmalloc()
用于当前堆内存不足时,需要向系统申请更多的内存
函数内调用了brk()和mmap()
释放空间
free --> __libc_free --> _int_free
_int_free()
检查 ----》是否fastbin ----》是fastbin,放至fastbin链表表头
+---》是否mmap分配 ----》 是,munmap_chunk()
+---》 否,合并chunk ----》 向低地址合并 ----》想高地址合并 ----》 下一个是否是top chunk ----》 是,合并到top chunk
+---》 否,合并加入unsorted bin
tcache
ubuntu 17.10中的新数据结构,提升了堆的性能,牺牲了一定的安全性能
堆溢出原理
条件:
- 可以写入堆
- 大小无限制
利用策略:
- 覆盖下一chunk的内容
- 利用堆机制实现任意地址写
要点
由低地址向高地址方向增长
堆管理器会对用户所申请的字节数进行调整,这也导致可利用的字节数都不小于用户申请的字节数
puts()内部会调用 malloc 分配堆内存
rwxp中的p权限是指隐私数据
一个程序一旦编译好后,text segment ,data segment 和 bss segment 是确定下来的
堆分配的最小单位是机器字长的2倍,32 位系统最小是 8 个字节,64 位系统最小是 16 个字节,比如 64 位系统执行malloc(0)会返回用户区域为 16 字节的块。
prev_size字段为8字节
strlen 是我们很熟悉的计算 ascii 字符串长度的函数,这个函数在计算字符串长度时是不把结束符 '\x00' 计算在内的,但是 strcpy 在复制字符串时会拷贝结束符 '\x00'
qword是四字节
申请一个超大的块,来使用 mmap 扩展内存。因为 mmap 分配的内存与 libc 之前存在固定的偏移因此可以推算出 libc 的基地址
堆分配函数
- malloc(size):见上文“堆操作”
- calloc(size):calloc 在分配后会自动进行清空,这对于某些信息泄露漏洞的利用来说是致命的
calloc(0x20);
//等同于
ptr=malloc(0x20);
memset(ptr,0,0x20);
- realloc(ptr,size)
当 realloc(ptr,size) 的 size 不等于 ptr 的 size 时
1)如果申请 size > 原来 size
----如果 chunk 与 top chunk 相邻,直接扩展这个 chunk 到新 size 大小
----如果 chunk 与 top chunk 不相邻,相当于 free(ptr),malloc(new_size)
2)如果申请 size < 原来 size
----如果相差不足以容得下一个最小 chunk(64 位下 32 个字节,32 位下 16 个字节),则保持不变
----如果相差可以容得下一个最小 chunk,则切割原 chunk 为两部分,free 掉后一部分
3)当 realloc(ptr,size) 的 size 等于 0 时,相当于 free(ptr)
当 realloc(ptr,size) 的 size 等于 ptr 的 size,不进行任何操作
危险函数标志
输入
gets,直接读取一行,忽略 '\x00'
scanf
vscanf
输出
sprintf
字符串
strcpy,字符串复制,遇到 '\x00' 停止
strcat,字符串拼接,遇到 '\x00' 停止
bcopy
调试方法
cat /proc/pid/maps 可以查看该进程内存映射
查看堆:
vmmap:查看堆地址
x/10gx addr:64位地址宽度以16进制打印10个地址内容
find xxxx:用来寻找你的输入内容
具体漏洞类型
- off_by_one 单字节溢出
1)溢出可控制任意字节:修改size来泄露或者覆盖其他数据
2)溢出NULL:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。
(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。
(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size(间接控制本chunk的size) ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的后一块(理论上是当前正在 unlink 的块)与当前正在 unlink 的块大小是否相等
【CTF】日志 2019.7.13 pwn 堆溢出基础知识的更多相关文章
- 日志学习系列(三)——NLog基础知识
前边我们解释了log4net的学习,我们再介绍一下NLog 一.什么是NLog NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码.NLog是一个简单 ...
- 数组、栈、堆(java基础知识五)
1.数组概述.定义格式 * A:数组概念 数组是存储同一种数据类型多个元素的集合.也可以看成是一个容器. 数组既可以存储基本数据类型,也可以存储引用数据类型. * B:数组定义格式 格式1:数据类型[ ...
- linux下堆溢出unlink的一个简单例子及利用
最近认真学习了下linux下堆的管理及堆溢出利用,做下笔记:作者作为初学者,如果有什么写的不对的地方而您又碰巧看到,欢迎指正. 本文用到的例子下载链接https://github.com/ctfs/w ...
- CTF必备技能丨Linux Pwn入门教程——stack canary与绕过的思路
Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...
- 今天maven install时碰到的两个问题(堆溢出和编译错误)
问题1.maven install时出现,日志如下: 系统资源不足.有关详细信息,请参阅以下堆栈追踪. java.lang.OutOfMemoryError: Java heap space at c ...
- 堆溢出学习笔记(linux)
本文主要是linux下堆的数据结构及堆调试.堆溢出利用的一些基础知识 首先,linux下堆的数据结构如下 /* This struct declaration is misleading (but a ...
- 转-CVE-2016-10190浅析-FFmpeg堆溢出漏洞
本文转载自CVE-2016-10190 FFmpeg Heap Overflow 漏洞分析及利用 前言 FFmpeg是一个著名的处理音视频的开源项目,使用者众多.2016年末paulcher发现FFm ...
- vmware漏洞之一——转:利用一个堆溢出漏洞实现VMware虚拟机逃逸
转:https://zhuanlan.zhihu.com/p/27733895?utm_source=tuicool&utm_medium=referral 小结: vmware通过Backd ...
- 见微知著(三):解析ctf中的pwn--Fastbin和bins的溢出
1月1号写博客,也是不容易呀!大家新年快乐呀! 先从Fastbin看起,是2015年RCTF的一道pwn题,shaxian.先看看代码的大致流程,随便输入一下: 这个题目关键之处在于堆溢出,对于堆种类 ...
- node.js运行内存堆溢出的解决办法
我是在将一组80多列13万多行的数据通过node-xlsx的时候出现的内存堆溢出的情况. 解决办法时将: node app.js 改成: node --max_old_space_size=10000 ...
随机推荐
- nuxt.js的生命周期
nuxt的生命周期分为客户端生命周期,服务端生命周期 1.服务端的生命周期 执行顺序为:nuxtServerlnit(store,context){}, 类似于vue.js中的main.js.可以在 ...
- 第16章 发布和部署应用程序(ASP.NET Core in Action, 2nd Edition)
本章包括 发布 ASP.NET Core 应用程序 在 IIS 中托管 ASP.NET Core 应用程序 自定义 ASP.NET Core 应用程序的 URL 通过捆绑和缩小优化客户端资源 到目前为 ...
- WebService基本功能接口实现
一.web服务的创建 1.新建一个web服务 创建一个空的模板可以为后面添加服务 2.创建一个服务 3.创建好服务后可以在websevice里面添加一个接口方法 4.我们运行下我们写的简单接口方法是否 ...
- react 微信h5跳转小程序
componentDidMount() { this.getWxConfig() } getWxConfig () { // 请求后台接口拿到 data信息 wx.config({ debug: fa ...
- flink 启动job命令
0. 启动flink-session ./bin/yarn-session.sh -n 4 -s 3 -jm 2048 -tm 6144 高版本 bin/yarn-session.sh -d -s 3 ...
- Think Python 学习笔记
#!/usr/bin/env python# coding: utf-8# # Think Python 学习笔记# 1.关于异或计算符# In[2]:6^2# 2.关于函数# 注意:变量名称不能用数 ...
- node报错解决办法
依次报错: Error: Cannot find module 'gifsicle' Syntax Error: Error: Cannot find module 'imagemin-gifsicl ...
- maven使用junit测试报class not found
突然就找不到类,查了一下是因为使用了Clean的命令,把编译好的class文件清理掉了,在Maven中使用test进行测试就可以了
- C#清空控件的值
/// 清除容器里面某些控件的值 /// </summary> /// <param name="parContainer">容器类控件</param ...
- liunx服务器搭建jenkins环境
服务器搭建jenkins 持续集成环境(1)-Jenkins安装 1)安装JDK Jenkins需要依赖JDK,所以先安装JDK1.8 yum install java-1.8.0-openjdk* ...