from flask import Flask, render_template, request, redirect, url_for, session import uuid import subprocess import re import os import random from captcha.image import ImageCaptcha from waitress import serve import base64 import configparser import configparser # Load from config.ini config = configparser.ConfigParser() config.read("config.ini") secretkey = config.get("HectaMail", "secretkey") captchachars = config.get("HectaMail", "captchachars") # Status report print("HectaMail is starting up...") print("Your secret key is:", secretkey) print("Your CAPTCHA allowed characters are:", captchachars) # Define the allowed pattern for the username allowed_pattern = r'^[a-zA-Z0-9.]+$' # Function to generate the CAPTCHA Code def generate_captcha_text(): # characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' captcha_text = ''.join(random.choice(captchachars) for i in range(6)) return captcha_text # Function to determine if the username is compilent with the regex filter def is_valid_input(input_string): return re.match(allowed_pattern, input_string) is not None # Initalise Flask app = Flask(__name__) app.secret_key = secretkey # Function to create the email account, in this case using doas for security def create_email_account(username, password): if password and is_valid_input(username): try: # Create a temporary file to escape the password with open("tmp/password.tmp", "w") as file: file.write(password) # Pass the file through a shell command cmd = ["cat", "tmp/password.tmp", "|", "doas", "-u", "maddy", "maddy", "creds", "create", f"{username}@hectabit.org"] # Run and determine the result of the shell command result = subprocess.run(" ".join(cmd), shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Delete the temporary file os.remove("tmp/password.tmp") # Check if command executed correctly if result.returncode == 0: # Command executed successfully return True else: # Handle errors, log them, and return False error_message = result.stderr.decode("utf-8") print(f"Error creating email account: {error_message}") return False except Exception as e: # Handle exceptions and return False print(f"Error creating email account: {str(e)}") return False else: # Something went very wrong if this function triggers print(f"Injection Bypass! Very bad!") return False @app.route('/') def index(): # Generate a unique token unique_token = str(uuid.uuid4()) # Generate the CAPTCHA for the user captcha_text = generate_captcha_text() image = ImageCaptcha().generate(captcha_text) # Store the CAPTCHA and token in the session session['captcha_text'] = captcha_text session['unique_token'] = unique_token # Encode the image in base64 image_base64 = base64.b64encode(image.getvalue()).decode('utf-8') # Report the CAPTCHA print(captcha_text) # Pass the CAPTCHA through to index.html return render_template('index.html', captcha_image=image_base64, unique_token=unique_token) @app.route('/api', methods=['POST']) def register(): # Get the form data username = request.form.get('username') password = request.form.get('password') # Get the CAPTCHA user_captcha = request.form.get('captcha') # Get the unique token submitted_token = request.form.get('unique_token') # Check if the submitted token matches the one in the session if submitted_token != session.get('unique_token'): # Token mismatch, handle accordingly return "Token Expired", 400 # Generate a new unique token for the next request session['unique_token'] = str(uuid.uuid4()) # Report the user captcha result print(user_captcha) # Check the regex filter if not is_valid_input(username): return render_template('num.html'), 400 # Validate the captcha captcha_text = session.get('captcha_text', '') print(captcha_text) if user_captcha.lower() != captcha_text.lower(): # CAPTCHA incorrect return render_template('captcha_err.html'), 400 # Attempt to create the email if create_email_account(username, password): # Email created return render_template('ok.html') else: # Backend error, potentially maddy return render_template('err.html'), 500 # Start the web server if __name__ == '__main__': serve(app, host='0.0.0.0', port=8050)