FormulaOne: Level 3 -> 5

Mar 1, 2025

🚨

Spoiler alert: This post contains spoilers for overthewire's FormulaOne game. If you would like to solve this challenge on your own, then don't read ahead.

Background

This is the sixth and final part in a series of posts on overthewire's FormulaOne game. If you'd like to read the earlier posts, you can do so here:

Challenge Code

/*
 * -( nemo1.c )-
 * by nemo 2005
 *
 * v0.2
 *
 * Bit of fun, for #social.
 *
 * Thanks to hawkes for working on this with me
 * to make it exploitable.
 *
 * http://www.pulltheplug.org (now overthewire.org)
 */

#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);       // weak :P
        
        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");
        printf("[!] feel free to remove this check, and let me know if you get it working.\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;
}

The Game Ends Here

Unfortunately this writeup will be short because this challenge only works on a specific GCC version. Take a look at this block in the main() function:

if((long)&buf2 > (long)&mfptrr) {
    printf("[!] Sorry, it's unlikely you can exploit this with your version of gcc.\n");
    printf("[!] feel free to remove this check, and let me know if you get it working.\n");
    exit(1);
}

This challenge only works if the stack is arranged such that mfptrr is a lower address than buf2. Modern GCC versions will place buffers (buf2) at a higher address than pointer variables (mfptrr).

This challenge would need to be recompiled in an older version of GCC for it to be exploitable.

It's a bit anti-climatic that can't get to the last challenge (formulaone6) but sometimes that's just the nature of CTF challenges!

I've learned a lot along the way and will be on the lookout for similarly challenging CTF's - which means, more writeups, eventually!

Ryan McIntire