l1ncctf

Newbies that want to learn.

HSCTF 9 - Gallery

A simple web challenge involving directory traversal.

Description

Look at these images I found!

Points: 201

Solves: 303

URL: http://web1.hsctf.com:8003/

Additional attachments: Download

Solution

I believed it was a steganography challenge at first, so I wgetted all of the images on the site and then ran exiftool, but I came back with absolutely nothing.

Until on closer inspection at the gallery page, I noticed that the image url had /image?image= and I immediately knew it could be exploited as a path traversal attack due to the images being embedded through the image parameter instead of directly embedding the file (which is more secure than that lol).

I looked at the source code and noticed this route

@app.route("/flag")
def flag():
	if 2 + 2 == 5:
		return send_file("/flag.txt")
	else:
		return "No.", 400

This was a clear indication of where the flag is located, but there were a few checks I had to go through.

@app.route("/image")
def image():
	if "image" not in request.args:
		return "Image not provided", 400
	if ".jpg" not in request.args["image"]:
		return "Invalid filename", 400
	
	file = IMAGE_FOLDER.joinpath(Path(request.args["image"]))
	if not file.is_relative_to(IMAGE_FOLDER):
		return "Invalid filename", 400
	
	try:
		return send_file(file.resolve())
	except FileNotFoundError:
		return "File does not exist", 400

So according to these checks it had to be a .jpg and had to be relative to the directory where the images are located. After seeing the directory relativity check I knew that it could be bypassed with a ../ and so that became apart of my payload as

../flag.txt

Now I needed a way to bypass the .jpg check, at first I tried ideas like

../flag.txt.jpg

with a null character in between .txt and .jpg, but that clearly didn’t work. Until I looked back at the source code and it hit me, the .jpg didnt have to be at the end of the file, all it needed was to be inside the request parameter, so I crafted

../.jpg/../flag.txt

and voilà, I have the flag.

Written on May 10, 2022