from flask import Flask, g, request, render_template, redirect, current_app from uuid import uuid4 from flask_argon2 import Argon2 from Crypto.Cipher import ARC4 from binascii import hexlify, unhexlify import sqlite3 import json import secrets app = Flask(__name__) argon2 = Argon2(app) flag = 'HM{h3r35_y0ur_2time_p4d}' database = 'wep.sqlite3' def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = sqlite3.connect(database) return db @app.teardown_appcontext def close_connection(exception): db = getattr(g, '_database', None) if db is not None: db.close() def check_session(cookies): if 'session' not in cookies and 'iv' not in cookies or 'uid' not in cookies: return False try: cur = get_db().execute('SELECT uid, username, iv, key FROM users WHERE uid = ? AND iv = ? LIMIT 1', (cookies['uid'], cookies['iv'])) cur_user = cur.fetchone() except sqlite3.Error as e: print(e) return 'Something went wrong, ping the admins' if not cur_user: return False uid = cur_user[0] username = cur_user[1] iv = cur_user[2] key = cur_user[3] cipher = ARC4.new(bytes(iv) + key) try: plaintext = cipher.decrypt(unhexlify(cookies['session'])) session = json.loads(plaintext) except: return False session['key'] = key session['iv'] = iv return session @app.route('/', methods=['GET']) def index(): return render_template('index.html') @app.route('/register', methods=['GET']) def show_register(): return render_template('register.html') @app.route('/register', methods=['POST']) def register(): if 'username' not in request.form or 'password' not in request.form or 'password2' not in request.form: return 'All fields are required!' username = request.form['username'] password = request.form['password'] password2 = request.form['password2'] if not username.isalnum(): return 'Username must be alphanumeric' if password != password2: return 'Password confirmation does not match' if len(password) < 6: return 'Password must be at least 6 chars!' try: cur = get_db().execute('SELECT username FROM users WHERE username= ? LIMIT 1', (username,)) cur_user = cur.fetchone() except: return 'Something went wrong, ping the admins' if cur_user: return 'Username already exist' try: cur.execute('INSERT INTO users (uid, username, password, iv, key) VALUES(?, ?, ?, 0, ?)', (str(uuid4()), username, argon2.generate_password_hash(password), secrets.token_bytes(16),)) get_db().commit() except sqlite3.Error as e: print(e) return 'Something went wrong, ping the admins' return redirect('/login', 302) @app.route('/login', methods=['GET']) def show_login(): return render_template('login.html') @app.route('/login', methods=['POST']) def login(): if 'username' not in request.form or 'password' not in request.form: return 'All fields are required!' username = request.form['username'] password = request.form['password'] if not username.isalnum(): return 'Login failed' if len(password) < 6: return 'Login failed' try: cur = get_db().execute('SELECT uid, username, password, iv, key FROM users WHERE username = ? LIMIT 1', (username,)) cur_user = cur.fetchone() except sqlite3.Error as e: print(e) return 'Something went wrong, ping the admins' if not cur_user: return 'Login failed' if not argon2.check_password_hash(cur_user[2], password): return 'Login failed' uid = cur_user[0] username = cur_user[1] iv = cur_user[3]+1 key = cur_user[4] if iv >= 255: iv = 0 data = { 'username': username, 'description': 'No description yet', 'show_flag': False } cipher = ARC4.new(bytes(iv) + key) try: ciphertext = cipher.encrypt(json.dumps(data).encode('ascii')).hex() except: return 'Something went wrong, ping the admins' try: get_db().execute('UPDATE users SET iv = ? WHERE username = ?', (iv, username,)) get_db().commit() except sqlite3.Error as e: print(e) return 'Something went wrong, ping the admins' response = current_app.make_response(redirect('/user', 302)) response.set_cookie('session', value=str(ciphertext), path='/', httponly=True) response.set_cookie('uid', value=uid, path='/', httponly=True) response.set_cookie('iv', value=str(iv), path='/', httponly=True) return response @app.route('/user', methods=['GET']) def show_user(): session = check_session(request.cookies) if not session: return redirect('/login', 302) if session['show_flag']: return flag return render_template('user.html', authenticated=True, username=session['username'], description=session['description']) @app.route('/user', methods=['POST']) def user(): session = check_session(request.cookies) if not session: return redirect('/login', 302) if session['show_flag']: return flag if 'description' not in request.form: return 'Description field is mandatory!' description = request.form['description'] if len(description) < 10 or len(description) > 200: return 'Description either too short (<10) or too long (>200)' username = session['username'] key = session['key'] iv = session['iv']+1 if iv >= 255: iv = 0 data = { 'username': username, 'description': description, 'show_flag': False } cipher = ARC4.new(bytes(iv) + key) try: ciphertext = cipher.encrypt(json.dumps(data).encode('ascii')).hex() except: return 'Something went wrong, ping the admins' try: get_db().execute('UPDATE users SET iv = ? WHERE username = ?', (iv, username,)) get_db().commit() except sqlite3.Error as e: print(e) return 'Something went wrong, ping the admins' response = current_app.make_response(redirect('/user', 302)) response.set_cookie('session', value=str(ciphertext), path='/', httponly=True) response.set_cookie('iv', value=str(iv), path='/', httponly=True) return response return render_template('user.html', authenticated=True, username=session['username'], description=session['description']) @app.route('/logout', methods=['GET']) def logout(): response = current_app.make_response(redirect('/', 302)) for cookie in request.cookies: response.set_cookie(cookie, value='', expires=0) return response if __name__ == "__main__": app.run(host='0.0.0.0')