Avatar
Part time CTF Player learn every day!!
🌠 I Love Hoshimachi Suisei!! 🌠
🌠 I Love Hoshimachi Suisei!! 🌠

OverTheWire FormulaOne Level 4 → 5 tutorial!!

Login

Connect to the FormulaOne game as formulaone4:

ssh formulaone4@formulaone.labs.overthewire.org -p 2232
# password: (from previous level)

Task

Find an exploit in the formulaone5 binary that gives us the password for user formulaone5.


Analyzing the Code

Here’s the vulnerable program (nemo1.c), left behind on the server:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

#define NBUFSIZ 1024

char *buf,*brrr;
void (*mfptrr)();
char buf2[NBUFSIZ];

void func1(char *arg)
{
    char envar[NBUFSIZ+1];
    strncpy(envar,arg,NBUFSIZ);
    envar[NBUFSIZ] = 0;
    printf("[*] Environment variable: %s\n",envar);
    return;
}

void int_handler()
{
    if(strlen(buf) >= NBUFSIZ -1) {
        exit(1);
    }
    memcpy(buf2,buf,strlen(buf)-1);
    printf("[+] Local buffer: %s.\n",buf2);
    mfptrr(0);
}

void cont_handler()
{
    printf("[+] :D\n");
    mfptrr(0);
}

void check_main(char **av)
{
    int a,b,c;
    char *home;
    long key;

    if(home = getenv("HOME")) {
        if(home[1]) {
            a = rand();
            b = (int)home[0];
            c = (int)home[1];
            key = a + b + c;
        }
    }

    if(key == 0xdeadbeef) {
        signal(SIGINT, int_handler);
        signal(SIGCONT, cont_handler);

        if(getenv("TIMER")) sleep(1);
        
        buf = malloc(NBUFSIZ + 1);
        strncpy(buf,av[1],NBUFSIZ);
        buf[NBUFSIZ] = 0;
    }

    return;
}

int main(int ac, char **av, char **env)
{
    char **tmp = env,*loc_brrr;
    mfptrr = exit;

    srand(0xcafebabe);

    if((long)&buf2 > (long)&mfptrr) {
        printf("[!] Sorry, it's unlikely you can exploit this with your version of gcc.\n");
        exit(1);
    }

    if(getenv("BUFFER")) buf = strdup(getenv("BUFFER"));
    if(getenv("TERM")) brrr = strdup(getenv("TERM"));

    while(*(++tmp)) func1(*tmp);
    check_main(av);

    return 1;
}

Theory

This program sets up signal handlers (SIGINT, SIGCONT) and allows interaction with buffers of fixed size (buf2, envar). There is clearly a chance for buffer overflow / pointer overwrite.

However, the crucial piece is this runtime check:

if((long)&buf2 > (long)&mfptrr) {
    printf("[!] Sorry, it's unlikely you can exploit this with your version of gcc.\n");
    exit(1);
}

This ensures the relative stack layout must match a specific pattern (pointer mfptrr below buf2). On modern GCC / glibc builds, this layout is different, so the exploit path doesn’t work anymore.

That means the challenge is tied to a specific compiler and memory layout from when the wargame was first released.


Solution

  1. The intended exploit path is to trigger a buffer overflow and overwrite the function pointer mfptrr so that it points to malicious code.
  2. Unfortunately, the stack check aborts execution on modern systems:

    [!] Sorry, it's unlikely you can exploit this with your version of gcc.
    
  3. This effectively blocks us unless we recompile with an older GCC that arranged variables differently.

So while we can inspect and understand the vulnerable code, we can’t practically exploit it on the current OTW servers.


Conclusion

This is the last playable level of FormulaOne. The intended exploit (overwriting the function pointer after manipulating buffers + signals) only works on certain legacy GCC versions.

Because of that, the OverTheWire admins left the challenge code up as a curiosity, but it cannot be solved under the current server configuration.

It’s a slightly anti-climactic ending, but that’s the nature of older wargames — sometimes compilers and protections evolve, breaking old challenges.


Thanks for reading!

Until next time — Otsumachi!! 💖☄️✨

Cinema

all tags

GOT-overwrite aboutme aead ai alphanumeric-shellcode apt argc0 argon2 aslr assembly asymmetric atoi automation backbox bandit base64 bash beginner behemoth binary binary-exploitation binary-to-ascii blackarch blind blind-sqli blogging blue-team bruteforce buffer-overflow buffer-overwrite c caesar canary capabilities checksec command-injection commonmark cookie cron crypto cryptography ctf cutter cyberchef cybersecurity defenders detection dev directory-traversal dnf docs drifter ecc education elf env envp exploitation finale forensics format-string formulaone frequency frequency-analysis gcc gdb getchar gfm ghidra github-pages governance gpg guide hashing hkdf http jekyll jmpbuf kali kasiski kdf kernel keylength kramdown krypton lab ld_preload leviathan lfi lfsr linux linux-syscall llmops log-poisoning ltrace manpage markdown maze memcpy mitigations mitmproxy mlops narnia natas networking newline-injection nonce nop-sled nx object-injection obsidian openssl osint overflow overthewire package-manager pacman parrot path path-hijacking pathname php pie pkc pki pointer-trick pqc priv-esc privilege-escalation provable-security pwn pwntools pyshark python race-condition radare2 rag randomness recon red-team redirect relro requests ret2env ret2libc reverse-engineering reversing ricing roadmap rop rot13 rsa scapy security seed seo serialization session setjmp-longjmp setuid shell shellcode smoke soc sockets sprintf sql-injection srop stack-canary stack-overflow strace strcmp strcpy streamcipher strings strncpy strtoul substitution suid suisei symlink symmetric terminal test threat-intel time-based tls troubleshooting tshark type-juggling ubuntu udp utumno vigenere virtualbox virtualization vmware vortex walkthrough web windows wireshark writing wsl x86
dash theme for Jekyll by bitbrain made with