FlaskForm Pharmaceuticals

Challenge Overview

We are given a Flask web challenge. On /products, each More Info button sends a request containing a file= parameter.

Intercepting the request with Burp shows that the application loads files from the assets directory and the parameter is vulnerable to Path Traversal.

Example:

file=assets/panacea.txt

By modifying it, we can traverse directories:

../../../etc/passwd #Found current UID
../../proc/self/status #Found current user based on the UID

Path traversal works, but reading ../flag.txt fails due to insufficient permissions.

In the source code, there is a compiled C binary called readflag:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    setuid(0);
    FILE *fp;
    char ch;

    fp = fopen("/app/flag.txt", "r");
    if (fp == NULL)
    {
        printf("Error in opening file\n");
        return 0;
    }

    while ((ch = fgetc(fp)) != EOF)
        printf("%c", ch);
    printf("\n");
    fclose(fp);
    return 0;
}

Important observations:

  • setuid(0); → the binary runs as root

  • It reads /app/flag.txt

  • If we can execute this binary, we get the flag

We found this line in server.py file

This exposes the Werkzeug Debug Console.


Exploitation

For this kind of exploitation, this arrow-up-rightcould be a powerful reference.

To access /debug, we need the Werkzeug PIN. The PIN is generated from a combination of public and private bits.

Public bits:

  • werkzeug

  • flask.app

  • Flask

  • /usr/local/lib/python3.11/site-packages/flask/app.py (default path)

Private bits:

  • MAC address from /sys/class/net/eth0/address (converted to decimal)

  • Machine ID /etc/machine-id was empty, so we used the alternative location: /proc/sys/kernel/random/boot_id

Using these values, we reconstructed the PIN with:

The script generated the correct Werkzeug debug PIN, allowing access to:

Note: The debug console endpoint can be either /console or /debug depending on the Werkzeug version. In this challenge, /console was the correct one. It can also easily be find with a fuzzing tool, like dirsearch.

After entering the PIN, we obtained the interactive Python console.

Inside the console:

List files:

Execute the binary:

Because readflag calls setuid(0), it runs as root and reads /app/flag.txt, printing the flag directly in the console.

Note: Important paths for this challenge

/proc/self/status - for current UID

/etc/passwd - for finding the current user via UID

/sys/class/net/eth0/address - MAC Adress

machine-id paths:

/etc/machine-id

/proc/sys/kernel/random/boot_id - fallback

made by k0d

Last updated