Author:cxing
Date:2023年5月12日
GLIBC 2.35中的Unlink
众所周知,glibc的堆管理器主要用链表结构维护chunk,特别的对于bins中双向链表的脱链操作叫做unlink。在老版本的glibc中,unlink被定义为一个宏,而新版本glibc中unlink被定义为一个函数。
关于为什么会从宏变成函数,我个人猜测有两方面原因。一是函数有编译时检查,而宏没有;二是现代编译器对小函数的优化得很好了,可能直接类似宏展开,并不会有额外的函数调用开销。简言之,unlink的作用就是脱链,通常需要对双链表结构的bins取出时chunk时会调用unlink。
除了及早期的unlink,现在glibc中的unlink通常有两个检查。一个是对size字段的检查,一个是对fd和bk指针的检查。对size字段的检查并不难处理,难的是fd和bk指针的检查使,为了绕过该检查使得unlink attach的威力下降了一大截。
/* Take a chunk off a bin list. */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
漫步unlink
通常 unsafe unlink都是向前合并,因此你需要找到如下图所示的内存布局或者构造出如下图所示的内存布局,你才能进行unsafe unlink。
而现代unsafe unlink已经不像曾经那么强大了,其最终结果是向S写入S-3,即[S]=S-3。但是许多讲unlink都在说可以任意地址写是怎么回事?我认为这个说法导致了很多人学习unlink很费劲,本质上unsfae link并不能任意地址写,只能如我上句所说进行[S]=S-3的写入操作。那么如何才能任意地址写呢?
想要unsafe unlink的基础上进行任意地址写,需要劫持一个可以进行写操作的指针变量,因此若我们能够通过控制S指针从S-3开始往高地址覆写,直到劫持一个可以可控的进行写操作的指针为止,我们才算完成任意地址写,例如S+1是一个有写操作的指针,那么我们覆盖到S+1劫持该指针就能任意地址写了。

static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd

文章详细介绍了glibc堆管理器中的unlink操作,从宏到函数的转变,以及其在内存安全中的作用。它涉及到对chunk的双链表维护,以及如何通过unlink进行内存布局的操纵。文中还提到了一个2014年的HITCON栈溢出漏洞利用案例,展示了如何利用unlink实现任意地址写,并讨论了PartialRELRO下的漏洞利用策略。
最低0.47元/天 解锁文章
302

被折叠的 条评论
为什么被折叠?



