好好说话之House Of Einherjar
前言
又鸽了好久,抱歉哈~ 总的来说House Of Einherjar这种利用方法还是挺简单的,有点像chunk extend/shrink技术,只不过该技术是后向,并且利用top_chunk合并机制,个人觉得杀伤力比较强大
往期回顾:
(补题)HITCON 2018 PWN baby_tcache超详细讲解
好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc
(补题)LCTF2018 PWN easy_heap超详细讲解
好好说话之Tcache Attack(3):tcache stashing unlink attack
好好说话之Tcache Attack(2):tcache dup与tcache house of spirit
好好说话之Tcache Attack(1):tcache基础与tcache poisoning
好好说话之Large Bin Attack
好好说话之Unsorted Bin Attack
好好说话之Fastbin Attack(4):Arbitrary Alloc
(补题)2015 9447 CTF : Search Engine
好好说话之Fastbin Attack(3):Alloc to Stack
好好说话之Fastbin Attack(2):House Of Spirit
好好说话之Fastbin Attack(1):Fastbin Double Free
好好说话之Use After Free
好好说话之unlink
…
编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ
House Of Einherjar
House Of Einherjar这种堆块的利用方式与之前的有些区别,该技术可以强制使得malloc
返回一个几乎任意地址的chunk。和之前的利用技术有一些区别,之前我们都是尽可能的避免释放堆块与top_chunk合并,因为释放之后的堆块可能会进行复用,比如在挂hook的时候我们需要两次对同一个释放堆块进行写操作。但是House Of Einherjar这种技术反而是,当释放堆块下一个块是top_chunk的时候,free
会与相邻后向地址进行合并
涉及的原理
free函数:后向合并
free函数后向合并关键代码如下(glibc/malloc/malloc.c):
解读一下这段代码:
- 4002:判断被释放堆块p的inuse标志位是否为
0
,如果为0则进行if中的内容,相当于一个检查。通过这个点说明我们至少要通过堆溢出去覆盖掉相邻高地址位的inuse
标志位,最常见的方式就是off-by-one
- 4003:记录相邻堆块p的
prev_size
值 - 4004:size为size + prev_size
- 4005:堆块p的指针最后由
chunk_at_offset()
函数决定,chunk_at_offset()函数如下图,作用是将原本p指针位置加上
s偏移后的位置作为合并堆块的新指针。那么带回到free函数中,意思就是原本p指针需要减去
(向后)一个后向堆块size(p->prev_size)大小的偏移后得到合并堆块的新指针 - 4006:unlink检查
free函数:与top_chunk合并
当被释放堆块紧邻top_chunk,那么释放后会与top_chunk进行合并
可以看到执行set_head()函数后,合并堆块的size会变为两个堆块的总和,并且top_chunk的指针会指向被合并的堆块p的位置。就相当于top_chunk把p给吞了,并取代了p的位置
how2heap例子验证
由于wiki上面那个例子讲真没看懂,所以在how2heap里面找了找,发现了有这个例子,下面是我精简之后的程序源码:
1 //gcc -g hollk.c -o hollk2 //glibc-2.233 4 #include <stdio.h>5 #include <stdlib.h>6 #include <string.h>7 #include <stdint.h>8 #include <malloc.h>9 10 int main()11 {12 setbuf(stdin, NULL);13 setbuf(stdout, NULL);14 15 uint8_t* a;16 uint8_t* b;17 uint8_t* d;18 19 a = (uint8_t*) malloc(0x38);20 printf("a: %p\n", a);21 22 int real_a_size = malloc_usable_size(a);23 printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding:%#x\n", real_a_size);24 25 size_t fake_chunk[6];26 27 fake_chunk[0] = 0x100;28 fake_chunk[1] = 0x100;29 fake_chunk[2] = (size_t) fake_chunk;30 fake_chunk[3] = (size_t) fake_chunk;31 fake_chunk[4] = (size_t) fake_chunk;32 fake_chunk[5] = (size_t) fake_chunk;33 printf("Our fake chunk at %p looks like:\n", fake_chunk);34 35 b = (uint8_t*) malloc(0xf8);36 int real_b_size = malloc_usable_size(b);37 printf("b: %p\n", b);38 39 uint64_t* b_size_ptr = (uint64_t*)(b - 8);40 printf("\nb.size: %#lx\n", *b_size_ptr);41 a[real_a_size] = 0;42 printf("b.size: %#lx\n", *b_size_ptr);43 44 size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);45 printf("Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);46 *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;47 48 fake_chunk[1] = fake_size;49 50 free(b);51 printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);52 53 d = malloc(0x200);54 printf("Next malloc(0x200) is at %p\n", d);55 }
调试一下
首先在第25行下断点,执行下面的代码:
12 setbuf(stdin, NULL);13 setbuf(stdout, NULL);14 15 uint8_t* a;16 uint8_t* b;17 uint8_t* d;18 19 a = (uint8_t*) malloc(0x38);20 printf("a: %p\n", a);21 22 int real_a_size = malloc_usable_size(a);23 printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rou nding: %#x\n", real_a_size);
这段代码我们关注的点其实就是创建了一个size为0x48
(0x38+0x10)大小的chunk_a
接下来我们在第35行下断点,执行下面的代码:
25 size_t fake_chunk[6];26 27 fake_chunk[0] = 0x100;28 fake_chunk[1] = 0x100;29 fake_chunk[2] = (size_t) fake_chunk;30 fake_chunk[3] = (size_t) fake_chunk;31 fake_chunk[4] = (size_t) fake_chunk;32 fake_chunk[5] = (size_t) fake_chunk;33 printf("Our fake chunk at %p looks like:\n", fake_chunk);
这里创建了一个数组fake_chunk,并将其中填满数据,在地3行会输出fake_chunk的地址,使用gdb看一下这个位置
这里将fake_chunk的prev_size、size部分设置为0x100,fd、bk、fd_nextsize、bk_nextsize设置为fake_chunk自身地址,这样做是为了绕过free()函数后向合并时最后的unlink检查
接下来我们在第39行下断点,执行下面的代码:
35 b = (uint8_t*) malloc(0xf8);36 int real_b_size = malloc_usable_size(b);37 printf("b: %p\n", b);
这段代码其实就是创建了一个size为0x108(0xf8+0x10)大小的chunk_b,我们来看一下:
接下来将断点下在第41行,完成uint64_t* b_size_ptr = (uint64_t*)(b - 8);
这段代码:
这里其实就是将chunk_b的malloc指针-0x8的位置,即chunk_b的size值放在了b_size_ptr变量中。这一步是为了更好的演示接下来溢出后的对比
那么接下来将断点下载第44行,我们看一下a[real_a_size] = 0;
这段diamante运行之后的结果
a[real_a_size] = 0
这段代码中的a[n]是以chunk_a
的malloc指针为起始的指针数组
,那么数组下标n指向的就是第n+1
个字节的地址,也就是说a[real_a_size]其实指向的是以chunk_a的malloc指针为起始,第real_a_size + 1个字节的位置等于0。其实这里模拟的就是off-by-one的过程。那么这样一来chunk_b的inuse标志位就被覆盖成了0
接下来我们在第46行下断点,执行size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
,分析一下这个计算过程:
fake_size是由b-sizeof(size_t)*2和(uint8_t*)fake_chunk相减得到的:
- b-sizeof(size_t)*2:chunk_b的malloc指针减去两个地址位宽,也就是chunk_b的头指针
- (uint8_t*)fake_chunk:即是伪造堆块的头指针
那么这样一来就可以很明显的看出fake_size,即是chunk_b头指针距离fake_chunk头指针的偏移,需要注意的是我们看到的偏移为0xffffd5555575a140
这代表着偏移其实是一个负数
接下来我们将断点下在第50行,执行下面的部分代码:
46 *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;47 48 fake_chunk[1] = fake_size;
real_a_size-sizeof(size_t)
的位置其实就是chunk_a与chunk_b公用的chunk_b的prev_size位置,也就是说这一步模拟的是通过对chunk_a的data赋值后,影响chunk_b的prev_size,根据第46行的代码,我们可以知道chunk_b的prev_size背负上了fake_size。接下来又将fake_chunk的size部分也修改成了fake_size
将chunk_b与fake_chunk部署完成后,像上图一样摆在一起,这看起来就有那味儿了
。chunk_b的prev_size等于fake_chunk的size
,这个size恰巧又是chunk_b到fake_chunk的偏移
,更巧的是chunk_b的inuse标志位为0
那么如果chunk_b被释放掉,首先会去检查其inuse标志位,发现为0
,这就意味着存在一个相邻地址的堆块也是处于释放状态
的,那么就会根据chunk_b的prev_size先前找是否存在一个大小为0xffffd5555575a140
大小的堆块,结果根据chunk_b的头指针+0xffffd5555575a140
处找到了fake_chunk
,fake_chunk的size
正是我们部署的0xffffd5555575a140
根据free()函数后向合并机制,由于我们部署了fake_chunk的fd
、bk
、fd_nextsize
、bk_nextsize
,所以可以绕过unlink
检查,那么chunk_b与fake_chunk就被合并称为一个大小为fake_size + b_size
的大堆块,并且合并大堆块的头指针
即是fake_chunk的头指针0x00007fffffffdf00
这还不算完,根据top_chunk合并机制,由于chunk_b是紧邻top_chunk
的,那么在chunk_b与fake_chunk合并之后top_chunk会将合并后的大堆块整个“吞掉”
。新的top_chunk的size变成了old_top_size + fake_size + b_size
。并且top_chunk的头指针会变成合并堆块的头指针,即fake_chunk的头指针0x00007fffffffdf00
接下来我们将断点下在第54行,执行free(b)
和malloc(0x200)
这两步操作。free(b)会完成上述的执行过程,而因为bin中没有
能够满足malloc(0x200)的空闲块,所以会向top_chunk
申请一个size为0x210
(0x200+0x10)大小的堆块。由于此时top_chunk的头指针是fake_chunk0x00007fffffffdf00
,所以最后被启用的堆块即是以fake_chunk为头指针0x00007fffffffdf00
,size为0x210大小的堆块
这里由于pwndbg插件的heap指令无法识别heap段意外的区域,所以我们直接运行结束,打印出这个从top_chunk申请的堆块的头指针:
可以看到,我们伪造的fake_chunk就会被正式以堆块的形式被启用了
总结
利用该方法需要注意的三点
- 需要有溢出漏洞可以写物理相邻的高地址的 prev_size 与 PREV_INUSE 部分
- 需要计算目的 fake_chunk 与 chunk_b 地址之间的差,所以需要泄漏地址
- 需要在目的 chunk 附近构造相应的 fake chunk,从而绕过 unlink 的检测
好好说话之House Of Einherjar相关推荐
- 如何看当前windows是utf8还是gbk_职场中的OKR如何“好好说话”
在工作中,经常会遇到各种各样的问题需要沟通,不管是团队内部的,还是跨部分,或者是对上级汇报还是管理下属.我们发现,有些能力很优秀的人,他们讲的内容,别人很快能理解.但是有的人,说了半天,大家也不知道他 ...
- ⊱人永远需要两种能力:好好说话和情绪稳定
昨晚已经躺下,收到朋友的一条微信. 是一张截图,题目是:武昌火车站附近爆发社会恶劣事件--面馆老板因与食客发生口角冲突被砍头断臂,事后扔进垃圾桶. 现场十分血腥,被围观群众拍下迅速在微博等网络媒体上传 ...
- 读书笔记之《好好说话》
作者 主创成员:马东.马薇薇.黄执中.周玄毅.邱晨.胡渐彪.刘京京. 感想 这本书的主创者是<奇葩说>的大牛们,这也是将它纳入书单的原因. 不管在生活中还是在工作中"好好说话& ...
- 好好说话之Tcache Attack(1):tcache基础与tcache poisoning
进入到了Tcache的部分,我还是觉得有必要多写一写基础的东西.以往的各种攻击手法都是假定没有tcache的,从练习二进制漏洞挖掘的角度来看其实我们一直模拟的都是很老的环境,那么这样一来其实和真正的生 ...
- 好好说话之Fastbin Attack(1):Fastbin Double Free
好像拖更了好久...实在是抱歉....主要是fastbin attack包含了四个部分,后面的例题不知道都对应着哪个方法,所以做完了例题才回来写博客.fastbin attack应该也会分四篇文章分开 ...
- 沟通技巧-《好好说话》书中的精髓:掌握沟通、说服、谈判、演讲、辩论的五维话术,让你在任何场景下,都能做到处变不惊,学会说话这个技术活。
<好好说话>书中的精髓:掌握沟通.说服.谈判.演讲.辩论的五维话术,让你在任何场景下,都能做到处变不惊,学会说话这个技术活. 相信在生活中,每个人都可能因为不会说话遇到一些困难: 工作辛苦 ...
- 好好说话之Use After Free
到了Use After Free啦,总体来说这种手法并不复杂,特征也很明显,就是在静态分析阶段观察释放chunk之后指针是否置空.本以为参加hw会往后拖更,没想到这么快就写完了.如果前面一直跟着学的话 ...
- 好好说话之unlink
堆溢出的第三部分unlink,这可能是有史以来我做的讲解图最多的一篇文章了累死 .可能做pwn的人都应该听过unlink,见面都要说声久仰久仰.学unlink的时候走了一些弯路,也是遇到了很多困扰的问 ...
- 好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc
前言 本来是在做tcache attack的例题的,但是wiki上的challenge2考察的重点不仅仅是在tcache.题目程序中没有输出的功能,所以无法像往常一样去泄露libc,这个时候就需要进行 ...
最新文章
- 【Qt】在ubuntu14.04上安装Qt5.12(失败)
- python学习之- 内置函数
- Python的setuptools详解【1】
- C#深入解析Json格式内容
- JVM调优系列:(四)GC垃圾回收
- HDU 1542:Atlantis(扫描线+线段树 矩形面积并)***
- 一生都学不完的计谋(经典)
- 贝佐斯首次展示月球着陆器 蓝色起源2024年载人登月
- 启动后显示不了数据_90后都买不起房?统计数据显示:90后成了城市租房主力!...
- 本机找不到workstation 和 Computer Browser服务
- 生产力系统的四类要素
- xapian_binaries
- 项目进度计划的基本方法
- 透视特洛伊木马程序开发技术(转)
- 字节跳动前端实习面试经历分享
- 回调地狱[Callback Hell]
- 加菲猫台词 (请对号入座-:))
- 金立 M6 (GN8003) 解锁 BootLoader 进入第三方 recovery 刷机 ROOT
- Java 实现位运算计算加减乘除
- 为什么我的公众号没有留言功能呢?
热门文章
- Centos7.6部署docker容器虚拟化平台(Container 容器端口映射)
- 【Business Touch Kit】服务号消息发送接口返回401如何解决?
- 单调栈解决取矩形问题
- 3GGO够酷够绚,但仅支持Nokia
- 有关C、C++的有用网站
- Android性能优化一绘制原理分析
- 科学道德与学术诚信 计算机,科学道德与学术诚信论文.pdf
- Java 中(hash 0x7FFFFFFF)问题 哈希表中数组下标的计算
- 接口和抽象类的区别与使用场景
- 微软Office办公软件哪个版本更好用?office365,office2022,2023等