workshop/insecure-app/app.py (62 lines of code) (raw):

from flask import Flask, request, render_template_string, make_response from flask_basicauth import BasicAuth import subprocess import os import sqlite3 import json import pprint app = Flask(__name__) app.config['BASIC_AUTH_USERNAME'] = os.environ.get('AUTH_USERNAME') app.config['BASIC_AUTH_PASSWORD'] = os.environ.get('AUTH_PASSWORD') basic_auth = BasicAuth(app) @app.route('/', methods=['GET']) def index(): response = make_response('Nothing to see here') response.headers['Content-Type'] = 'text/plain' return response @app.route('/crash', methods=['GET']) def crash(): output = "SOMETHING WENT WRONG\n\n" for i, l in os.environ.items(): output += i + ':' + l + "\n" output += "ENV=" + str(os.environ) + "\n\n" output += str(request.headers) + "\n\n" output += str(request.endpoint) + "\n\n" output += str(request.method) + "\n\n" output += str(request.remote_addr) + "\n\n" response = make_response(output) response.headers['Content-Type'] = 'text/plain' return response @app.route('/admin', methods=['GET', 'POST']) @basic_auth.required def admin(): output = '' # SQL Injection? db = sqlite3.connect("tutorial.db") cursor = db.cursor() username = '' password = '' try: #the % is what makes it bad, instead of passing them in as parameters #Example Exploit: SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '' OR '1'='1' cursor.execute("SELECT * FROM users WHERE username = '%s' AND password = '%s'" % (username, password)) except: pass if request.method == 'POST': if 'command' in request.form: cmd = request.form['command'] process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode == 0: output = stdout.decode('utf-8') else: output = f"Error (Exit Code: {process.returncode}):\n{stderr.decode('utf-8')}" elif 'file' in request.files: uploaded_file = request.files['file'] uploaded_file.save(os.path.join('/uploads', uploaded_file.filename)) output = f"File {uploaded_file.filename} uploaded successfully!" elif 'sql' in request.form: sql = request.form['sql'] res = cursor.execute(sql) output = json.dumps(res.fetchall()) return render_template_string(""" <h1>Admin panel. If your name is not Frank, you should not be here.</h1> <form action="/admin" method="post"> Run a command: <input type="text" name="command"> <input type="submit" value="Run"> </form> <br> <form action="/admin" method="post" enctype="multipart/form-data"> Upload a file: <input type="file" name="file"> <input type="submit" value="Upload"> </form> <br> <form action="/admin" method="post"> Inject some SQL <input type="text" name="sql"> <input type="submit" value="Run"> </form> <pre>{{output}}</pre> """, output=output) if __name__ == '__main__': app.run(host='0.0.0.0', port=8080, debug=True)