首页
社区
课程
招聘
[原创]large Bin Attack学习(_int_malloc源码细读 )
发表于: 2024-3-9 15:37 11805

[原创]large Bin Attack学习(_int_malloc源码细读 )

2024-3-9 15:37
11805

参考文章:
wiki:Large Bin Attack - CTF Wiki (ctf-wiki.org)
源码级调试glibc:源码级调试glibc_glibc cannot be compiled without optimization-CSDN博客
源码分析:glibc 2.31 malloc与free 源码分析(持续更新) - PwnKi - 博客园 (cnblogs.com)+glibc malloc源码分析 - PwnKi - 博客园 (cnblogs.com)
详细拆分了_int_malloc的流程 并且按照功能分了标题 想要了解对应部分就直接点击标题跳转即可
第一次阅读glibc的源码然后进行分析 有错误的地方请大佬指正

每次去看别人文章分析总结的 总感觉比较难记住 每个libc版本的区别 然后也没彻底理解一些操作 所以进行阅读源码

然后重点是检查机制部分 如果只想看重点就直接跳转到largebin入链操作

然后在正式阅读源码之前 我们先理清楚largebin的结构(去除了头部的fd_nextsize/bk_nextsize 为了图片干净一点)

largebin_struct

我们可以简化一下 去除尾链的fd和头链的bk方便我们理清逻辑

large_struct

大概就是这个样子 也就是bin头部通过fd/bk链接chunk size链表的头部和尾部 然后chunk size链表之间通过fd_nextsize/bk_nextsize链接

chunksize链表中 同一个大小的chunk通过fd/bk进行链接

所以largebin的fd和bk和其他的双向链不同我们不能通过从bin一路通过fd返回到large bin的头部

后面的操作中最重要的就是Victim变量 这个变量是当前循环到的unsortedbin chunk<br>bck变量 也就是bck <-------> victim 这个关系

从unsorted_chunk最后一位开始遍历 直到碰到unsorted_bin的头部 我在这里 没很确定是否unsortedbin可不可以指向自己 我们可以调试看看

然后查看chunk结构

查看unsortedbin的大小

可以发现fd bk都是指向的unsortedbin中第一个chunk 我们清空unsortedbin看看

这里的安全机制全是对unsortedbin中的chunk进行的检查

不能小于2*SIZE_SZ不能大于av->system_men也就是该分配去的内存分配总量

对next chunk(物理意义上的紧挨着)也进行一样的操作

mchunkptr next = chunk_at_offset (victim, size); /* 获得指向内存空间中当前 chunk 的下一个chunk 的指针 */

检查next chunk的prev_size 是否等于当前的chunk size

size = chunksize (victim); /* 计算 size */

检查bck的fd是否为当前chunk 或者当前chunk的fd是否是bin的头结点

bck = victim->bk;

victim = unsorted_chunks (av)->bk)

应该就是检查下一个chunk是否是合法的

检查当前chunk是否是free的 通过next chunk的p值

然后就是从unsortedbin割small chunk 如果符合条件

所需chunk大小在smallbin的范围之内

bck为unsortedbin的头 也就是unsortedbin中仅有一个chunk

victim为last remainder chunk 也就是分割过一次

大小刚好大于所需nb大小+Minsize(这里猜测就是一个最小chunk 这样才能切割)

满足以上条件 就直接分割 然后将victim返回给用户

在这里已经将chunk从unsortdbin中移除

如果chunk和当前需要的chunk大小一致 则直接返回chunk 并且设置物理意义上紧挨着的下一个chunk的size中p为0也就是free状态

如果开启了tcache机制 且tcache未满则将chunk放入tcache中

然后直接返回

这里主要是将unsortedbin合并后的 入small链表或者large链表的操作

这里的fwd和bck记好了 我们从unsortedbin抠出来的chunk就要合并进入fwd和bck的中间

这后面的操作往往是先让fwd到指定的位置 然后bck通过fwd->bk来进行的定位

这里把最后的部分 直接提前 拿出来 因为smallbin和largebin的入链操作都含这个代码

largebin还有chunk size的入链操作 以及其他的复杂检查

如果属于small bin则进行fwd和bck的赋值

small bin 的链表表头赋值给 bck:bck = bin_at (av, victim_index);

首个chunk赋值给fwd :fwd = bck->fd;

如果属于large_bins同理进行赋值 然后判断该插入什么合适的位置

因为largebin是按照大小进行的排序 由大到小 所以最小的在链表最后

判断large是否有空闲chunk:

如果当前chunk比最后一位chunk还小则直接加入链表末尾

bck是头 bck->bk应该就是最后一位

然后要加入fwd和bck之间 我们应该先调整fwd和bck 所以bck改为链表最后一位 fwd改为链表头

bck<----->chunk<----->fwd

否则进行遍历判断 匹配第一个小于等于 当前chunk的

如果该chunk与当前chunk相同则让chunk插入fwd之后 所以

因为large bin是按照大小进行的排序 所以我们为了不额外修改chunk size链表 直接将chunk链接到fwd后面

当我们需求的chunk size大于large中所有的chunk size的情况 执行largebin的入chunk_size链操作:<span id = "attack"></span>

这里我理解的是largebin存在两条链 也就是chunk size的链 和fd bk构成的bins链 这里先是入的chunk size的链

这里就是重点了 也就是large bin的入链操作

首先这是初始状态

status01status1status1status1

让bck等于fwd->bk 也就是把bck提到fwd前方 并且进行安全检查

最后就是执行入链操作了

在一开始的时候提过

largebin情况

首先是判断情况 我们只处理这一种情况:largebin中有chunk 然后largebin中最大的chunk大于我们的需求

接下来的代码都是从largebin中获取chunk

取最小的chunk 反方向循环 找到刚好大于等于我们所需chunk size的 chunk

如果一个大小的chunk链表中有多个chunk 优先取第二个 不轻易改变chunk size链表的值

chunk 通过unlink脱链 remainder chunk入unsortedbin链

安全检查 是否切割后的chunk大于minsize 与安全检查 largebin第一个chunk和头的互锁状态

我是大概浏览的 大概意思是去剩下的chunk中寻找 如果没找到就去topchunk分配 如果topchunk不够就去系统申请

我们主要是利用:largechunk中最大的chunk还是小于我们所需求的chunk大小这种情况 我们来详细分析一下这个流程中究竟干了什么

我们可以发现 这里的代码 危险的地方在于 如果现在我们能够修改largebin中fwd位置的chunk 我们就能够泄露victim的地址

我们主要利用这两行代码

如何实现?比如

while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)){
    bck = victim->bk;
    size = chunksize (victim);  /* 计算 size */
    // ...
}
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)){
    bck = victim->bk;
    size = chunksize (victim);  /* 计算 size */
    // ...
}
unsortedbin
all: 0x555555559680 —▸ 0x7ffff7fb9be0 (main_arena+96) ◂— 0x555555559680
unsortedbin
all: 0x555555559680 —▸ 0x7ffff7fb9be0 (main_arena+96) ◂— 0x555555559680
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x555555559680
Size: 0x90 (with flag bits: 0x91)
fd: 0x7ffff7fb9be0
bk: 0x7ffff7fb9be0
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x555555559680
Size: 0x90 (with flag bits: 0x91)
fd: 0x7ffff7fb9be0
bk: 0x7ffff7fb9be0
pwndbg> tel  0x7ffff7fb9be0
00:0000│ rdx r10 r11 0x7ffff7fb9be0 (main_arena+96) —▸ 0x5555555597a0 ◂— 0x0
01:0008│             0x7ffff7fb9be8 (main_arena+104) ◂— 0x0
02:0010│             0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
03:0018│             0x7ffff7fb9bf8 (main_arena+120) —▸ 0x555555559680 ◂— 0x0
04:0020│             0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
05:0028│             0x7ffff7fb9c08 (main_arena+136) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
06:0030│             0x7ffff7fb9c10 (main_arena+144) —▸ 0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
07:0038│             0x7ffff7fb9c18 (main_arena+152) —▸ 0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
pwndbg> tel  0x7ffff7fb9be0
00:0000│ rdx r10 r11 0x7ffff7fb9be0 (main_arena+96) —▸ 0x5555555597a0 ◂— 0x0
01:0008│             0x7ffff7fb9be8 (main_arena+104) ◂— 0x0
02:0010│             0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
03:0018│             0x7ffff7fb9bf8 (main_arena+120) —▸ 0x555555559680 ◂— 0x0
04:0020│             0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
05:0028│             0x7ffff7fb9c08 (main_arena+136) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
06:0030│             0x7ffff7fb9c10 (main_arena+144) —▸ 0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
07:0038│             0x7ffff7fb9c18 (main_arena+152) —▸ 0x7ffff7fb9c00 (main_arena+128) —▸ 0x7ffff7fb9bf0 (main_arena+112) —▸ 0x555555559680 ◂— 0x0
if (__glibc_unlikely (size <= 2 * SIZE_SZ)
              || __glibc_unlikely (size > av->system_mem))
            malloc_printerr ("malloc(): invalid size (unsorted)");
if (__glibc_unlikely (size <= 2 * SIZE_SZ)
              || __glibc_unlikely (size > av->system_mem))
            malloc_printerr ("malloc(): invalid size (unsorted)");
if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)|| __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
     malloc_printerr ("malloc(): invalid next size (unsorted)");
if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)|| __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
     malloc_printerr ("malloc(): invalid next size (unsorted)");
/* 如果 next chunk 中记录前一个 chunk 大小的 prev_size 与 size 不符,则报错 */
if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
    malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
/* 如果 next chunk 中记录前一个 chunk 大小的 prev_size 与 size 不符,则报错 */
if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
    malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
if (__glibc_unlikely (bck->fd != victim)
              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
            malloc_printerr ("malloc(): unsorted double linked list corrupted");
if (__glibc_unlikely (bck->fd != victim)
              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
            malloc_printerr ("malloc(): unsorted double linked list corrupted");
/* 如果 next chunk 中的显示前一个 chunk 是否正在使用的标志位为1,*/
/* 即前一个 chunk 正在使用,则报错 */
if (__glibc_unlikely (prev_inuse (next)))
    malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
/* 如果 next chunk 中的显示前一个 chunk 是否正在使用的标志位为1,*/
/* 即前一个 chunk 正在使用,则报错 */
if (__glibc_unlikely (prev_inuse (next)))
    malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
if (in_smallbin_range (nb) &&
    bck == unsorted_chunks (av) &&
    victim == av->last_remainder &&
    (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
    remainder_size = size - nb;
    remainder = chunk_at_offset (victim, nb);
    unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
    av->last_remainder = remainder;
    remainder->bk = remainder->fd = unsorted_chunks (av);
    if (!in_smallbin_range (remainder_size)){
        remainder->fd_nextsize = NULL;
        remainder->bk_nextsize = NULL;
    }
    set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head (remainder, remainder_size | PREV_INUSE);
    set_foot (remainder, remainder_size);
 
    check_malloced_chunk (av, victim, nb);
    void *p = chunk2mem (victim);
    alloc_perturb (p, bytes);
    return p;
}
if (in_smallbin_range (nb) &&
    bck == unsorted_chunks (av) &&
    victim == av->last_remainder &&
    (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
    remainder_size = size - nb;
    remainder = chunk_at_offset (victim, nb);
    unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
    av->last_remainder = remainder;
    remainder->bk = remainder->fd = unsorted_chunks (av);
    if (!in_smallbin_range (remainder_size)){
        remainder->fd_nextsize = NULL;
        remainder->bk_nextsize = NULL;
    }
    set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head (remainder, remainder_size | PREV_INUSE);
    set_foot (remainder, remainder_size);
 
    check_malloced_chunk (av, victim, nb);
    void *p = chunk2mem (victim);
    alloc_perturb (p, bytes);
    return p;
}
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
set_inuse_bit_at_offset (victim, size);
set_inuse_bit_at_offset (victim, size);
if (tcache_nb && tcache->counts[tc_idx] < mp_.tcache_count){
    tcache_put (victim, tc_idx);
    return_cached = 1;
    continue;
}
if (tcache_nb && tcache->counts[tc_idx] < mp_.tcache_count){
    tcache_put (victim, tc_idx);
    return_cached = 1;
    continue;
}
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;  /* 返回内存指针 */
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;  /* 返回内存指针 */
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
if (in_smallbin_range (size)){
    victim_index = smallbin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;
}
if (in_smallbin_range (size)){
    victim_index = smallbin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;
}
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
if (fwd != bck)
if (fwd != bck)
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;
}
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;
}
while ((unsigned long) size < chunksize_nomask (fwd)){
    fwd = fwd->fd_nextsize;
    assert (chunk_main_arena (fwd));
}
while ((unsigned long) size < chunksize_nomask (fwd)){
    fwd = fwd->fd_nextsize;
    assert (chunk_main_arena (fwd));
}
if ((unsigned long) size== (unsigned long) chunksize_nomask (fwd))
    fwd = fwd->fd;
if ((unsigned long) size== (unsigned long) chunksize_nomask (fwd))
    fwd = fwd->fd;
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
    malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
    malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
bck = fwd->bk;
if (bck->fd != fwd)
    malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
bck = fwd->bk;
if (bck->fd != fwd)
    malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
mark_bin (av, victim_index);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2024-3-11 00:45 被ElegyYuan0x1编辑 ,原因: 重新更改图片 让文字显示出来
收藏
免费 7
支持
分享
最新回复 (1)
雪    币: 3303
活跃值: (30941)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-3-10 16:11
2
游客
登录 | 注册 方可回帖
返回
//