Skip to content

House of lore

复制本地路径 | 在线编辑

不推荐看的方法,很强行很教条

详细说明

Step1. 准备数据

第一,由于用到是 smallbin,老套路在后面需要把 tcache 先塞满,所以要先准备七个块,用于后续塞满 tcache。第二,准备一个 smallbin 大小的块,这个块就是后续被攻击的块。第三,准备一个大块,避免前面所说的块在 free 的时候和 top chunk 合并了。

然后就进行 free,首先被攻击的块会在 unsorted bin 中。我们再进行申请一个超大块,触发 unsorted → smallbin 这一过程。

// 用于填满 tcache 的块,没有其他意义
void *dummies[7];
for(int i=0; i<7; i++) dummies[i] = malloc(0x100);

// 用于被攻击的块
intptr_t *victim = malloc(0x100);

// 用于防止和 topchunk 合并的块
malloc(0x1000);

// 塞满 tcache
for(int i=0; i<7; i++) free(dummies[i]);

// 放入 unsorted bin
free(victim);

// 申请一个超大块,触发 unsorted -> smallbin 这一过程
malloc(0x1200);

// 最终:victim 在 smallbin 中

Step2. 尝试解链

现在我们希望从 smallbin 中解链时解出我们的目标地址。首先当然要先清空掉 tcache,这样申请对应大小块的时候会从 smallbin 中申请。

for(int i=0; i<7; i++) malloc(0x100);

现在我们尝试直接从 smallbin 中解链出目标,攻击的前提是我们可以对 victimbk 指针进行修改。如果我们简单地修改为 victim->bk = want,那么会报错:

victim->bk = want;

// 理想情况:small <--> victim <--> want
malloc(0x100);
want = malloc(0x100);

下面的代码是解链过程,他会检查 bck->fd 是否指向 victim

bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim)){
    errstr = "malloc(): smallbin double linked list corrupted";
    goto errout;
}

set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

为了避免这样的检查,我们需要构造一个中间块,这个块的 fd 指向 victim,这样在解链的时候就不会报错。所以这里表示了一个条件:我们需要有一个可以事先控制内容的内存,最常见的比如栈中有一片内存,程序又让你输入内容到这片内存中。

我们构造如下的格式,当我们进行 malloc(0x100) 取出 victim 的时候,此时 bck = victim->bk == can_control_addr1bin = smallbin_header,此时满足 bck->fd == victim

name    smallbin_header     victim                  can_control_addr1
--------------------------------------------------------------------
fd      victim              smallbin_header         victim
bk      victim              can_control_addr1       fakechunk

解链,即 bck->fd = bin; bin->bk = bck; 后,格式如下:

name    smallbin_header         can_control_addr1
--------------------------------------------------------------------
fd      victim                  smallbin_header
bk      can_control_addr1       fakechunk

Step3. stash 的影响

但上一步解链之后,malloc 有 stash 机制,即会把剩下的 smallbin 尽可能放入 tcache 中。而现在 tcache 是空的,所以是会不断进行这种操作的。

所以上一步之后,会尝试把 can_control_addr1 放入 tcache 中,放入之后,会继续沿着 bk 指针继续放入 tcache 中,所以下一个就是要把 fakechunk 放入 tcache 中。而如果 fakechunk->bk 不是 0,则会继续往下。所以脚本里面构造了 fakechunk 链条。


这个方法没有意义记录了。 脚本里面其实不止构造了一个中间块,还有另一个中间块。并且更关键的是,我把脚本改成只用一个中间块,发现居然不行,最后调试检查是因为释放块的时候有 set_inuse,由于中间块和 fakechunk 链条在附近,所以释放中间块的时候把 fakechunk 链条某个地址最后一位改了。而如果使用两个中间块,恰好偏了一点没改到。也就是说脚本其实挺凑巧的,第二个中间块没什么必要性。

另外这个方法也太极端太教条了,基本上不会遇到。举个最简单的证明:在 how2heap 中,这个方法居然没有 CTF 例题。

Comments