Python PIL is Sus - Amidst Us: HTB Cyber Apocalypse 2022
The AmidstUs tribe is a notorious group of sleeper agents for hire. We have plausible reasons to believe they are working with Draeger, so we have to take action to uncover their identities. Ulysses and bonnie have infiltrated their HQ and came across this mysterious portal on one of the unlocked computers. Can you hack into it despite the low visibility and get them access?
This was a fun challenge, mainly because the correct path was fairly obvious, although exploitation required a little googling. It was great practice for python exploitation.
The Challenge
We’re given a web page with amonglings (?) on them, as well as a place to upload a file

We can upload an image, and the page returns the image with the alpha changed to a value that we can specify here:

We’re also given the complete source code, which shows that this is a python flask app.
The Solution
From examining the page, we can see that we have one pathway through the code; via the /api/alphafy endpoint. In a flask app, you can typically find all the routes in a file named routes.py or something like that.
@api.route('/alphafy', methods=['POST'])
def alphafy():
if not request.is_json or 'image' not in request.json:
return abort(400)
return make_alpha(request.json)
This route just calls calls a function called make_alpha , which we can trace back to the utils.py file.
The make_alpha function is too long to include in it’s entirety, but here are a few snippets that are relevant:
def make_alpha(data):
color = data.get("background", [255, 255, 255])
try:
dec_img = base64.b64decode(data.get("image").encode())
image = Image.open(BytesIO(dec_img)).convert("RGBA")
img_bands = [band.convert("F") for band in image.split()]
alpha = ImageMath.eval(
f"""float(
max(
max(
max(
difference1(red_band, {color[0]}),
""" # just for syntax highlighting
# ...
# ...
# ...
new_image.save(buffer, format="PNG")
return {
"image": f"data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode()}"
}, 200
Examining this function, it looks like the call of ImageMath.eval might be interesting. When looking for vulnerabilites, just the word eval in any context is a huge red flag.
After some Googling, we figure out that something like CVE-2022-22817 might be interesting. Since we have a preliminary thumbs up, we can reach into our standard python bag of tricks and pull out a payload like:
__import__('os').system('cp /flag.txt /app/application/static')
Since it seems like the color parameters are evaluated, we can try exploiting the parameters using Burp.
First, we’ll send the api request to the Burp repeater:

Then, we’ll scroll to the bottom of the request, add our payload, and send it off:

We’ll see that we get a 400 response, indicating some kind of server error, but let’s check and see if we actually copied the flag to the /static directory:

Boom! We have a working PoC. Time to use it on the actual challenge server!

This time, I’m not using the repeater, just the interceptor.
After adding the exploit, we’ll pass it to the challenge server, and check if our exploit worked.

And we get the flag, solving the challenge and giving us access to the internal networks of the mysterious amonglings!
If this was interesting at all, helped you with one of your challenges, or if you have any questions or corrections, please drop me a line!