Google CTF 2017 mindreader

This is a write-up for the Google CTF 2017 “mindreader” challenge.

The mindreader webserver presented us with only a single input form:


Pretty much with the second term entered it was clear that any filename specified in the form will be read from the local disk. This sounded like an easy challenge.
Better yet, since also /etc/shadow was displayed, this meant that the process is probably running with root privileges. Jackpot!

The logical next step was to try to fetch the usual files you’d expect to contain anything useful: Service configuration files, shell histories, any log file or pretty much anything we could think of – but nothing useful was returned.

Some files that we expected to exist like /etc/ssh/sshd_config threw a 404 and others (/proc/cpuinfo) a 403.

The HTTPS server responded with a nginx server header but there was no sign of it anywhere either. Nor of any other webserver.

With /etc/issue and /etc/debian_version the system was identified as a Debian 8.8.
Digging into the Debian specific files we grabbed /var/log/apt/history.log:

In that file it showed that those packages were all installed in a single transaction:

git mercurial pkg-config wget python-pip python2.7 python2.7-dev 
python3.4 python3.4-dev build-essential libcurl4-openssl-dev libffi-dev 
libjpeg-dev libmysqlclient-dev libpng12-dev libpq-dev libssl-dev 
libxml2-dev libxslt1-dev swig zlib1g-dev gfortran libatlas-dev 
libblas-dev libfreetype6-dev liblapack-dev libquadmath0 
libmemcached-dev libsasl2-2 libsasl2-dev libsasl2-modules sasl2-bin

Searching for that specific package list leads us to the Google Cloud Platform – Python Runtime Docker Image:
https://github.com/GoogleCloudPlatform/python-runtime/blob/master/runtime-image/resources/apt-packages.txt

This explains the absence of pretty much any other service or configuration: We are attacking a Docker container.

The Dockerfile in that repository configures a work directory of /app. With that knowledge we can get the Python source code by requesting /app/main.py:

The source code revealed two key pieces of information:

  • There is an environment variable called “FLAG” (line 6).
  • Requests containing “proc” will always return a 403 (line 24).

Coincidentally environment variables are of course stored in /proc.

The filter looked solid and pythons open() does not accept wildcards. We cannot request anything containing “proc”.

But if there is a symlink pointing to a folder inside of /proc we wouldn’t need to request a file with “proc” in its name, we could traverse from there.

Quick research in a Vagrant VM showed that /dev/fd is a symlink to /proc/self/fd.
We requested /dev/fd/../environ:


And with that we finally got the flag.