Today’s challenge box is HackTheBoxes PetPet RcBee box. This is an easy box, and if you don’t fall down any rabbit holes, it’s a fairly quick one too. As usual, I will go through my steps to solving this box and try to explain my logic, my wins, and my failures, as I go.
Docker File
This box comes with a docker file so that we can run the challenge locally before going over to the live box. It also means we have access to the files that are used to build this website. We can view all the python, html, JavaScript and CSS files, so we can really look under the hood and figure out what’s going on. I’m not going to show you how to set it up, as you should know that. If not, a quick google will suffice.
The Website
Let’s have a look at the website I’m going to be targeting. It’s pretty cute:
As you can see, it has a file upload function. If I upload a picture, it will show me a GIF of the picture being petted by a hand, as below:
There is zero other functionality to this website. So now I’ll dig into the code and see what I can find.
Code Analysis
The first thing that that I noticed was that most of the core files were Python files. So immediately I knew this site was being built using a Python based framework, but which one?
I opened the /application/main.py file and noticed that it was importing flask. So we’re dealing with Flask – which is a Python based Web App Framework (I SHOULD have opened a different file at this point, but more on that later). In the same file I also came across the api endpoint at /api:
In the main.py file I discovered that the gifs being generated are stored in /app/application/static/petpets/ (This will be important later):
In the util.py file I noticed that there is a list of ‘Allowed Extensions’ for the upload function:
Like a Noob, which I am, I thought this was all I really needed to see. So I began to investigate the requests and responses being sent.
BURP Investigations
First, I uploaded a non-malicious, completely unremarkable, jpg file to see what happens. I got the following response:
The response is quite informative, as it not only shows us the file location, but also the name of the GIF file that it generates. So now I want to see that I can access the file directly using the filename and location:
Good. I can see it. This is good to know, and again, very useful later.
My Rabbit Hole
This is where I got myself a little side-tracked and maybe had a bit of a prolonged brain-fart. This happens more than I’d like to admit, and I include it because I want you dear reader, to know that learning this stuff is a matter of trial and error. There are lots of fore-head slaps involved. There are times when you will feel completely inadequate, and there are times when you will feel like a god-king. You just have to ride the wave.
What I did now was spend around an hour or more trying to upload a generic php shell by tricking the server into thinking it was a jpg file. How was I expecting a php shell to run on a Python website? I don’t know.
After some time, I gave up and had some tea.
After Tea Eureka (Eureka in the true sense, that I discovered this by mistake)
Once I returned to my PC I had another look at the docker files. I realized I hadn’t opened one file, the Dockerfile. Yes, I know, forehead slaps for days. Upon opening the Dockerfile I found some rather pertinent information that I had previously overlooked (by being a noob). Flask is using the Pillow (PIL) library (which is an image wrapper to speed up image loading), and docker also installs something called Ghostscript (v9.23). Ghostscript is an interpreter for PostScript and PDF files.
So I googled ghostscript vulnerabilities and one of the first results was exactly what I needed. The CVE is CVE-2018-16509 and it seems to allow you to upload a malicious jpeg that will trigger a buffer overflow and allow you to execute code on the server. There is a POC that I can try implement here:

This is where I paused, because I wanted to actually understand what I was doing. The code snippet above is written in PostScript (PostScript is a language used to communicate with printers and other devices). Seeing as I have never used PostScript and have no idea how it works, I asked ChatGPT to explain the code to me. Basically, this is a PostScript program that can be embedded in an Encapsulated PostScript File (EPS). According to ChatGPT, the interesting bit at the end that we are going to adapt for our purposes
“Defines a dictionary object that contains a key-value pair: OutputFile and “%pipe%touch /tmp/got_rce”. The OutputFile key is a PostScript system operator that sets the output destination for the current device. In this case, the value “%pipe%touch /tmp/got_rce” is set, which means that the output will be redirected to a Unix pipe”
So, from what I understand, instead of sending the output of this file to a printer, it will send the output to Unix in the form of a command. This allows an attacker to execute code or commands of our choosing. So now I need to edit this to create a payload for our scenario. I know that the flag is in the app/ directory, but I can’t read it there. So I need to move it to a folder where I’ll be able to read it. I know I can read files that are in static/petpets/ so I’ll try move it there:
%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: -0 -0 100 100 userdict /setpagedevice undef save legal { null restore } stopped { pop } if { legal } stopped { pop } if restore mark /OutputFile (%pipe%cp /app/flag /app/application/static/petpets/flag.txt) currentdevice putdeviceprops
Notice that I changed the name to make sure I can read it. Now I will send the payload in the form of a jpeg file called exp.jpg (I used nano to create it). Now all that’s left to do is visit http://0.0.0.0:1337/static/petpets/flag.txt and VOILA! We have the flag.