def get(self): data = request.get_json() challenge_id = data.get("challenge_id") try: challenge_id = int(challenge_id) except (ValueError, TypeError): return {"success": False, "error": "Invalid challenge id"} challenge = DockerChallenges.query.filter_by(id=challenge_id).first() if not challenge: return {"success": False, "error": "Invalid challenge"} user = get_current_user() account_id = user.account_id token = serialize({ "account_id": account_id, "challenge_id": challenge_id }) return { "success": True, "url": f"https://{INSTANCE}.pwn.college/download/{token}", }
def test_user_can_confirm_email(mock_smtp): """Test that a user is capable of confirming their email address""" app = create_ctfd() with app.app_context(): # Set CTFd to only allow confirmed users and send emails set_config('verify_emails', True) set_config('mail_server', 'localhost') set_config('mail_port', 25) set_config('mail_username', 'username') set_config('mail_password', 'password') register_user(app, name="user1", email="*****@*****.**") # Teams are not verified by default user = Users.query.filter_by(email='*****@*****.**').first() assert user.verified is False client = login_as_user(app, name="user1", password="******") # smtp.sendmail was called mock_smtp.return_value.sendmail.assert_called() with client.session_transaction() as sess: data = {"nonce": sess.get('nonce')} r = client.get('/challenges') assert r.location == "http://localhost/confirm" # We got redirected to /confirm r = client.get('http://localhost/confirm/' + serialize('*****@*****.**')) assert r.location == 'http://localhost/challenges' # The team is now verified user = Users.query.filter_by(email='*****@*****.**').first() assert user.verified is True destroy_ctfd(app)
def verify_email_address(addr): token = serialize(addr) text = """Please click the following link to confirm your email address for {ctf_name}: {url}/{token}""".format( ctf_name=get_config('ctf_name'), url=url_for('auth.confirm', _external=True), token=token) sendmail(addr, text)
def verify_email_address(addr): token = serialize(addr) text = """Please click the following link to confirm your email address for {ctf_name}: {url}/{token}""".format( ctf_name=get_config("ctf_name"), url=url_for("auth.confirm", _external=True), token=token, ) subject = "Confirm your account for {ctf_name}" return sendmail(addr=addr, text=text, subject=subject)
def forgot_password(email): token = serialize(email) text = """Did you initiate a password reset? If you didn't initiate this request you can ignore this email. Click the following link to reset your password: {0}/{1} """.format(url_for("auth.reset_password", _external=True), token) subject = "Password Reset Request from {ctf_name}" return sendmail(addr=email, text=text, subject=subject)
def forgot_password(email, team_name): token = serialize(team_name) text = """Did you initiate a password reset? Click the following link to reset your password: {0}/{1} """.format(url_for("auth.reset_password", _external=True), token) return sendmail(email, text)
def test_user_can_confirm_email(mock_smtp): """Test that a user is capable of confirming their email address""" app = create_ctfd() with app.app_context(), freeze_time("2012-01-14 03:21:34"): # Set CTFd to only allow confirmed users and send emails set_config("verify_emails", True) set_config("mail_server", "localhost") set_config("mail_port", 25) set_config("mail_useauth", True) set_config("mail_username", "username") set_config("mail_password", "password") register_user(app, name="user1", email="*****@*****.**") # Teams are not verified by default user = Users.query.filter_by(email="*****@*****.**").first() assert user.verified is False client = login_as_user(app, name="user1", password="******") r = client.get("http://localhost/confirm") assert "Need to resend the confirmation email?" in r.get_data( as_text=True) # smtp send message function was called if six.PY2: mock_smtp.return_value.sendmail.assert_called() else: mock_smtp.return_value.send_message.assert_called() with client.session_transaction() as sess: data = {"nonce": sess.get("nonce")} r = client.post("http://localhost/confirm", data=data) assert "confirmation email has been resent" in r.get_data( as_text=True) r = client.get("/challenges") assert (r.location == "http://localhost/confirm" ) # We got redirected to /confirm r = client.get("http://localhost/confirm/" + serialize("*****@*****.**")) assert r.location == "http://localhost/challenges" # The team is now verified user = Users.query.filter_by(email="*****@*****.**").first() assert user.verified is True r = client.get("http://localhost/confirm") assert r.location == "http://localhost/settings" destroy_ctfd(app)
def forgot_password(email): text = safe_format( get_config("password_reset_body") or DEFAULT_PASSWORD_RESET_BODY, ctf_name=get_config("ctf_name"), ctf_description=get_config("ctf_description"), url=url_for("auth.reset_password", data=serialize(email), _external=True), ) subject = safe_format( get_config("password_reset_subject") or DEFAULT_PASSWORD_RESET_SUBJECT, ctf_name=get_config("ctf_name"), ) return sendmail(addr=email, text=text, subject=subject)
def get_invite_code(self): from flask import current_app from CTFd.utils.security.signing import serialize, hmac secret_key = current_app.config["SECRET_KEY"] if isinstance(secret_key, str): secret_key = secret_key.encode("utf-8") team_password_key = self.password.encode("utf-8") verification_secret = secret_key + team_password_key invite_object = { "id": self.id, "v": hmac(str(self.id), secret=verification_secret), } code = serialize(data=invite_object, secret=secret_key) return code
def verify_email_address(addr): text = safe_format( get_config("verification_email_body") or DEFAULT_VERIFICATION_EMAIL_BODY, ctf_name=get_config("ctf_name"), ctf_description=get_config("ctf_description"), url=url_for("auth.confirm", data=serialize(addr), _external=True, _method="GET"), ) subject = safe_format( get_config("verification_email_subject") or DEFAULT_VERIFICATION_EMAIL_SUBJECT, ctf_name=get_config("ctf_name"), ) return sendmail(addr=addr, text=text, subject=subject)
def test_setup_integrations(): app = create_ctfd() with app.app_context(): register_user(app) user = login_as_user(app) r = user.get("/setup/integrations") assert r.status_code == 403 admin = login_as_user(app, "admin") r = admin.get("/setup/integrations") assert r.status_code == 403 admin = login_as_user(app, "admin") url = "/setup/integrations?state={state}&mlc_client_id=client_id&mlc_client_secret=client_secret&name=mlc".format( state=serialize(generate_nonce())) r = admin.get(url) assert r.status_code == 200 assert get_config("oauth_client_id") == "client_id" assert get_config("oauth_client_secret") == "client_secret" destroy_ctfd(app)
def forgot_password(email, team_name): token = serialize(team_name) text = """ <!DOCTYPE html> <html> <body style="margin: 0; font-family: 'Cabin', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif';color: #6e7b8a; text-align: center;"> <div style="background: #fff; margin: 0 auto; max-width: 550px;"> <p style="font-size: 1.25rem; font-weight: 200; line-height: 1.5rem;"> How to reset your password is as follows. </p> <p>Please click the following link to reset your password:</p> <a href="{0}/{1} style="background: #22b8eb; padding: 10px 20px 10px 30px; margin-bottom: 20px; color: #fff; font-size: .85rem; text-decoration: none; display: inline-block; text-align: center; cursor: pointer; border-radius: 5px;">Confirm Your Email</a> <br> <br><br><br><br>If you are not trying to reset your password, please ignore this email. It is possible that another user entered their login information incorrectly.<br> </div> </body> </html> """.format(url_for('auth.reset_password', _external=True), token) return sendmail(email, text)
def verify_email_address(addr): token = serialize(addr) text = """ <!DOCTYPE html> <html> <body style="margin: 0; font-family: 'Cabin', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif';color: #6e7b8a; text-align: center;"> <div style="background: #fff; margin: 0 auto; max-width: 550px;"> <p style="font-size: 1.25rem; font-weight: 200; line-height: 1.5rem;"> Welcome to {ctf_name}. </p> <p>Please click the following link to confirm your email address for {ctf_name}: </p> <a href="{url}/{token}" style="background: #22b8eb; padding: 10px 20px 10px 30px; margin-bottom: 20px; color: #fff; font-size: .85rem; text-decoration: none; display: inline-block; text-align: center; cursor: pointer; border-radius: 5px;">Confirm Your Email</a> <br>If you are not trying to confirm your email, please ignore this email. It is possible that another user entered their login information incorrectly.<br> <br><br><br><br>{info_add} </div> </body> </html> """.format(ctf_name=get_config('ctf_name'), url=url_for('auth.confirm', _external=True), token=token, info_add='Held by <a href="https://lancet.vip">Lancet</a>.') return sendmail(addr, text)
def get(self, challenge_id): if is_admin(): chal = Challenges.query.filter(Challenges.id == challenge_id).first_or_404() else: chal = Challenges.query.filter( Challenges.id == challenge_id, and_(Challenges.state != "hidden", Challenges.state != "locked"), ).first_or_404() chal_class = get_chal_class(chal.type) if chal.requirements: requirements = chal.requirements.get("prerequisites", []) anonymize = chal.requirements.get("anonymize") if challenges_visible(): user = get_current_user() if user: solve_ids = ( Solves.query.with_entities(Solves.challenge_id) .filter_by(account_id=user.account_id) .order_by(Solves.challenge_id.asc()) .all() ) else: # We need to handle the case where a user is viewing challenges anonymously solve_ids = [] solve_ids = set([value for value, in solve_ids]) prereqs = set(requirements) if solve_ids >= prereqs or is_admin(): pass else: if anonymize: return { "success": True, "data": { "id": chal.id, "type": "hidden", "name": "???", "value": 0, "category": "???", "tags": [], "template": "", "script": "", }, } abort(403) else: abort(403) tags = [ tag["value"] for tag in TagSchema("user", many=True).dump(chal.tags).data ] unlocked_hints = set() hints = [] if authed(): user = get_current_user() team = get_current_team() # TODO: Convert this into a re-useable decorator if is_admin(): pass else: if config.is_teams_mode() and team is None: abort(403) unlocked_hints = set( [ u.target for u in HintUnlocks.query.filter_by( type="hints", account_id=user.account_id ) ] ) files = [] for f in chal.files: token = { "user_id": user.id, "team_id": team.id if team else None, "file_id": f.id, } files.append( url_for("views.files", path=f.location, token=serialize(token)) ) else: files = [url_for("views.files", path=f.location) for f in chal.files] for hint in Hints.query.filter_by(challenge_id=chal.id).all(): if hint.id in unlocked_hints or ctf_ended(): hints.append( {"id": hint.id, "cost": hint.cost, "content": hint.content} ) else: hints.append({"id": hint.id, "cost": hint.cost}) response = chal_class.read(challenge=chal) Model = get_model() if scores_visible() is True and accounts_visible() is True: solves = Solves.query.join(Model, Solves.account_id == Model.id).filter( Solves.challenge_id == chal.id, Model.banned == False, Model.hidden == False, ) # Only show solves that happened before freeze time if configured freeze = get_config("freeze") if not is_admin() and freeze: solves = solves.filter(Solves.date < unix_time_to_utc(freeze)) solves = solves.count() response["solves"] = solves else: response["solves"] = None response["files"] = files response["tags"] = tags response["hints"] = hints db.session.close() return {"success": True, "data": response}
def get(self, challenge_id): if is_admin(): chal = Challenges.query.filter( Challenges.id == challenge_id).first_or_404() else: chal = Challenges.query.filter( Challenges.id == challenge_id, and_(Challenges.state != "hidden", Challenges.state != "locked"), ).first_or_404() try: chal_class = get_chal_class(chal.type) except KeyError: abort( 500, f"The underlying challenge type ({chal.type}) is not installed. This challenge can not be loaded.", ) if chal.requirements: requirements = chal.requirements.get("prerequisites", []) anonymize = chal.requirements.get("anonymize") # Gather all challenge IDs so that we can determine invalid challenge prereqs all_challenge_ids = { c.id for c in Challenges.query.with_entities(Challenges.id).all() } if challenges_visible(): user = get_current_user() if user: solve_ids = (Solves.query.with_entities( Solves.challenge_id).filter_by( account_id=user.account_id).order_by( Solves.challenge_id.asc()).all()) else: # We need to handle the case where a user is viewing challenges anonymously solve_ids = [] solve_ids = {value for value, in solve_ids} prereqs = set(requirements).intersection(all_challenge_ids) if solve_ids >= prereqs or is_admin(): pass else: if anonymize: return { "success": True, "data": { "id": chal.id, "type": "hidden", "name": "???", "value": 0, "solves": None, "solved_by_me": False, "category": "???", "tags": [], "template": "", "script": "", }, } abort(403) else: abort(403) tags = [ tag["value"] for tag in TagSchema("user", many=True).dump(chal.tags).data ] unlocked_hints = set() hints = [] if authed(): user = get_current_user() team = get_current_team() # TODO: Convert this into a re-useable decorator if is_admin(): pass else: if config.is_teams_mode() and team is None: abort(403) unlocked_hints = { u.target for u in HintUnlocks.query.filter_by( type="hints", account_id=user.account_id) } files = [] for f in chal.files: token = { "user_id": user.id, "team_id": team.id if team else None, "file_id": f.id, } files.append( url_for("views.files", path=f.location, token=serialize(token))) else: files = [ url_for("views.files", path=f.location) for f in chal.files ] for hint in Hints.query.filter_by(challenge_id=chal.id).all(): if hint.id in unlocked_hints or ctf_ended(): hints.append({ "id": hint.id, "cost": hint.cost, "content": hint.content }) else: hints.append({"id": hint.id, "cost": hint.cost}) response = chal_class.read(challenge=chal) solves_q, user_solves = _build_solves_query( extra_filters=(Solves.challenge_id == chal.id, )) # If there are no solves for this challenge ID then we have 0 rows maybe_row = solves_q.first() if maybe_row: challenge_id, solve_count = maybe_row solved_by_user = challenge_id in user_solves else: solve_count, solved_by_user = 0, False # Hide solve counts if we are hiding solves/accounts if scores_visible() is False or accounts_visible() is False: solve_count = None if authed(): # Get current attempts for the user attempts = Submissions.query.filter_by( account_id=user.account_id, challenge_id=challenge_id).count() else: attempts = 0 response["solves"] = solve_count response["solved_by_me"] = solved_by_user response["attempts"] = attempts response["files"] = files response["tags"] = tags response["hints"] = hints response["view"] = render_template( chal_class.templates["view"].lstrip("/"), solves=solve_count, solved_by_me=solved_by_user, files=files, tags=tags, hints=[Hints(**h) for h in hints], max_attempts=chal.max_attempts, attempts=attempts, challenge=chal, ) db.session.close() return {"success": True, "data": response}
def setup(): errors = get_errors() if not config.is_setup(): if not session.get("nonce"): session["nonce"] = generate_nonce() if request.method == "POST": # General ctf_name = request.form.get("ctf_name") ctf_description = request.form.get("ctf_description") user_mode = request.form.get("user_mode", USERS_MODE) set_config("ctf_name", ctf_name) set_config("ctf_description", ctf_description) set_config("user_mode", user_mode) # Style ctf_logo = request.files.get("ctf_logo") if ctf_logo: f = upload_file(file=ctf_logo) set_config("ctf_logo", f.location) ctf_small_icon = request.files.get("ctf_small_icon") if ctf_small_icon: f = upload_file(file=ctf_small_icon) set_config("ctf_small_icon", f.location) theme = request.form.get("ctf_theme", DEFAULT_THEME) set_config("ctf_theme", theme) theme_color = request.form.get("theme_color") theme_header = get_config("theme_header") if theme_color and bool(theme_header) is False: # Uses {{ and }} to insert curly braces while using the format method css = ( '<style id="theme-color">\n' ":root {{--theme-color: {theme_color};}}\n" ".navbar{{background-color: var(--theme-color) !important;}}\n" ".jumbotron{{background-color: var(--theme-color) !important;}}\n" "</style>\n" ).format(theme_color=theme_color) set_config("theme_header", css) # DateTime start = request.form.get("start") end = request.form.get("end") set_config("start", start) set_config("end", end) set_config("freeze", None) # Administration name = request.form["name"] email = request.form["email"] password = request.form["password"] name_len = len(name) == 0 names = Users.query.add_columns("name", "id").filter_by(name=name).first() emails = ( Users.query.add_columns("email", "id").filter_by(email=email).first() ) pass_short = len(password) == 0 pass_long = len(password) > 128 valid_email = validators.validate_email(request.form["email"]) team_name_email_check = validators.validate_email(name) if not valid_email: errors.append("Please enter a valid email address") if names: errors.append("That user name is already taken") if team_name_email_check is True: errors.append("Your user name cannot be an email address") if emails: errors.append("That email has already been used") if pass_short: errors.append("Pick a longer password") if pass_long: errors.append("Pick a shorter password") if name_len: errors.append("Pick a longer user name") if len(errors) > 0: return render_template( "setup.html", errors=errors, name=name, email=email, password=password, state=serialize(generate_nonce()), ) admin = Admins( name=name, email=email, password=password, type="admin", hidden=True ) # Create an empty index page page = Pages(title=None, route="index", content="", draft=False) # Upload banner default_ctf_banner_location = url_for("views.themes", path="img/logo.png") ctf_banner = request.files.get("ctf_banner") if ctf_banner: f = upload_file(file=ctf_banner, page_id=page.id) default_ctf_banner_location = url_for("views.files", path=f.location) # Splice in our banner index = f"""<div class="row"> <div class="col-md-6 offset-md-3"> <img class="w-100 mx-auto d-block" style="max-width: 500px;padding: 50px;padding-top: 14vh;" src="{default_ctf_banner_location}" /> <h3 class="text-center"> <p>A cool CTF platform from <a href="https://ctfd.io">ctfd.io</a></p> <p>Follow us on social media:</p> <a href="https://twitter.com/ctfdio"><i class="fab fa-twitter fa-2x" aria-hidden="true"></i></a> <a href="https://facebook.com/ctfdio"><i class="fab fa-facebook fa-2x" aria-hidden="true"></i></a> <a href="https://github.com/ctfd"><i class="fab fa-github fa-2x" aria-hidden="true"></i></a> </h3> <br> <h4 class="text-center"> <a href="admin">Click here</a> to login and setup your CTF </h4> </div> </div>""" page.content = index # Visibility set_config( ConfigTypes.CHALLENGE_VISIBILITY, ChallengeVisibilityTypes.PRIVATE ) set_config( ConfigTypes.REGISTRATION_VISIBILITY, RegistrationVisibilityTypes.PUBLIC ) set_config(ConfigTypes.SCORE_VISIBILITY, ScoreVisibilityTypes.PUBLIC) set_config(ConfigTypes.ACCOUNT_VISIBILITY, AccountVisibilityTypes.PUBLIC) # Verify emails set_config("verify_emails", None) set_config("mail_server", None) set_config("mail_port", None) set_config("mail_tls", None) set_config("mail_ssl", None) set_config("mail_username", None) set_config("mail_password", None) set_config("mail_useauth", None) # Set up default emails set_config("verification_email_subject", DEFAULT_VERIFICATION_EMAIL_SUBJECT) set_config("verification_email_body", DEFAULT_VERIFICATION_EMAIL_BODY) set_config( "successful_registration_email_subject", DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT, ) set_config( "successful_registration_email_body", DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY, ) set_config( "user_creation_email_subject", DEFAULT_USER_CREATION_EMAIL_SUBJECT ) set_config("user_creation_email_body", DEFAULT_USER_CREATION_EMAIL_BODY) set_config("password_reset_subject", DEFAULT_PASSWORD_RESET_SUBJECT) set_config("password_reset_body", DEFAULT_PASSWORD_RESET_BODY) set_config( "password_change_alert_subject", "Password Change Confirmation for {ctf_name}", ) set_config( "password_change_alert_body", ( "Your password for {ctf_name} has been changed.\n\n" "If you didn't request a password change you can reset your password here: {url}" ), ) set_config("setup", True) try: db.session.add(admin) db.session.commit() except IntegrityError: db.session.rollback() try: db.session.add(page) db.session.commit() except IntegrityError: db.session.rollback() login_user(admin) db.session.close() with app.app_context(): cache.clear() return redirect(url_for("views.static_html")) try: return render_template("setup.html", state=serialize(generate_nonce())) except TemplateNotFound: # Set theme to default and try again set_config("ctf_theme", DEFAULT_THEME) return render_template("setup.html", state=serialize(generate_nonce())) return redirect(url_for("views.static_html"))
def setup(): errors = get_errors() if not config.is_setup(): if not session.get("nonce"): session["nonce"] = generate_nonce() if request.method == "POST": # General ctf_name = request.form.get("ctf_name") ctf_description = request.form.get("ctf_description") user_mode = request.form.get("user_mode", USERS_MODE) set_config("ctf_name", ctf_name) set_config("ctf_description", ctf_description) set_config("user_mode", user_mode) # Style theme = request.form.get("ctf_theme", "core") set_config("ctf_theme", theme) theme_color = request.form.get("theme_color") if theme_color: # Uses {{ and }} to insert curly braces while using the format method css = ( ":root {{--theme-color: {theme_color};}}\n" ".navbar{{background-color: var(--theme-color) !important;}}\n" ".jumbotron{{background-color: var(--theme-color) !important;}}\n" ).format(theme_color=theme_color) set_config("css", css) # DateTime start = request.form.get("start") end = request.form.get("end") set_config("start", start) set_config("end", end) set_config("freeze", None) # Administration name = request.form["name"] email = request.form["email"] password = request.form["password"] name_len = len(name) == 0 names = Users.query.add_columns("name", "id").filter_by(name=name).first() emails = (Users.query.add_columns( "email", "id").filter_by(email=email).first()) pass_short = len(password) == 0 pass_long = len(password) > 128 valid_email = validators.validate_email(request.form["email"]) team_name_email_check = validators.validate_email(name) if not valid_email: errors.append("Please enter a valid email address") if names: errors.append("That user name is already taken") if team_name_email_check is True: errors.append("Your user name cannot be an email address") if emails: errors.append("That email has already been used") if pass_short: errors.append("Pick a longer password") if pass_long: errors.append("Pick a shorter password") if name_len: errors.append("Pick a longer user name") if len(errors) > 0: return render_template( "setup.html", errors=errors, name=name, email=email, password=password, state=serialize(generate_nonce()), ) admin = Admins(name=name, email=email, password=password, type="admin", hidden=True) # Index page index = """<div class="row"> <div class="col-md-6 offset-md-3"> <img class="w-100 mx-auto d-block" style="max-width: 500px;padding: 50px;padding-top: 14vh;" src="themes/core/static/img/logo.png" /> <h3 class="text-center"> <p>A cool CTF platform from <a href="https://ctfd.io">ctfd.io</a></p> <p>Follow us on social media:</p> <a href="https://twitter.com/ctfdio"><i class="fab fa-twitter fa-2x" aria-hidden="true"></i></a> <a href="https://facebook.com/ctfdio"><i class="fab fa-facebook fa-2x" aria-hidden="true"></i></a> <a href="https://github.com/ctfd"><i class="fab fa-github fa-2x" aria-hidden="true"></i></a> </h3> <br> <h4 class="text-center"> <a href="admin">Click here</a> to login and setup your CTF </h4> </div> </div>""".format(request.script_root) page = Pages(title=None, route="index", content=index, draft=False) # Visibility set_config("challenge_visibility", "private") set_config("registration_visibility", "public") set_config("score_visibility", "public") set_config("account_visibility", "public") # Verify emails set_config("verify_emails", None) set_config("mail_server", None) set_config("mail_port", None) set_config("mail_tls", None) set_config("mail_ssl", None) set_config("mail_username", None) set_config("mail_password", None) set_config("mail_useauth", None) set_config("setup", True) try: db.session.add(admin) db.session.commit() except IntegrityError: db.session.rollback() try: db.session.add(page) db.session.commit() except IntegrityError: db.session.rollback() login_user(admin) db.session.close() with app.app_context(): cache.clear() return redirect(url_for("views.static_html")) return render_template( "setup.html", nonce=session.get("nonce"), state=serialize(generate_nonce()), themes=config.get_themes(), ) return redirect(url_for("views.static_html"))
def get(self, challenge_id): if is_admin(): chal = Challenges.query.filter(Challenges.id == challenge_id).first_or_404() else: chal = Challenges.query.filter( Challenges.id == challenge_id, and_(Challenges.state != 'hidden', Challenges.state != 'locked') ).first_or_404() chal_class = get_chal_class(chal.type) if chal.requirements: requirements = chal.requirements.get('prerequisites', []) anonymize = chal.requirements.get('anonymize') if challenges_visible(): user = get_current_user() if user: solve_ids = Solves.query \ .with_entities(Solves.challenge_id) \ .filter_by(account_id=user.account_id) \ .order_by(Solves.challenge_id.asc()) \ .all() else: # We need to handle the case where a user is viewing challenges anonymously solve_ids = [] solve_ids = set([value for value, in solve_ids]) prereqs = set(requirements) if solve_ids >= prereqs or is_admin(): pass else: if anonymize: return { 'success': True, 'data': { 'id': chal.id, 'type': 'hidden', 'name': '???', 'value': 0, 'category': '???', 'tags': [], 'template': '', 'script': '' } } abort(403) else: abort(403) tags = [ tag['value'] for tag in TagSchema( "user", many=True).dump( chal.tags).data ] unlocked_hints = set() hints = [] if authed(): user = get_current_user() team = get_current_team() # TODO: Convert this into a re-useable decorator if config.is_teams_mode() and team is None: abort(403) unlocked_hints = set([ u.target for u in HintUnlocks.query.filter_by(type='hints', account_id=user.account_id) ]) files = [] for f in chal.files: token = { 'user_id': user.id, 'team_id': team.id if team else None, 'file_id': f.id, } files.append( url_for('views.files', path=f.location, token=serialize(token)) ) else: files = [ url_for('views.files', path=f.location) for f in chal.files ] for hint in Hints.query.filter_by(challenge_id=chal.id).all(): if hint.id in unlocked_hints or ctf_ended(): hints.append({ 'id': hint.id, 'cost': hint.cost, 'content': hint.content }) else: hints.append({'id': hint.id, 'cost': hint.cost}) response = chal_class.read(challenge=chal) Model = get_model() if scores_visible() is True and accounts_visible() is True: solves = Solves.query\ .join(Model, Solves.account_id == Model.id)\ .filter(Solves.challenge_id == chal.id, Model.banned == False, Model.hidden == False)\ .count() response['solves'] = solves else: response['solves'] = None response['files'] = files response['tags'] = tags response['hints'] = hints db.session.close() return { 'success': True, 'data': response }
def setup(): errors = get_errors() if not config.is_setup(): if not session.get("nonce"): session["nonce"] = generate_nonce() if request.method == "POST": # General ctf_name = request.form.get("ctf_name") ctf_description = request.form.get("ctf_description") user_mode = request.form.get("user_mode", USERS_MODE) set_config("ctf_name", ctf_name) set_config("ctf_description", ctf_description) set_config("user_mode", user_mode) # Style theme = request.form.get("ctf_theme", "core") set_config("ctf_theme", theme) theme_color = request.form.get("theme_color") theme_header = get_config("theme_header") if theme_color and bool(theme_header) is False: # Uses {{ and }} to insert curly braces while using the format method css = ( '<style id="theme-color">\n' ":root {{--theme-color: {theme_color};}}\n" ".navbar{{background-color: var(--theme-color) !important;}}\n" ".jumbotron{{background-color: var(--theme-color) !important;}}\n" "</style>\n" ).format(theme_color=theme_color) set_config("theme_header", css) # DateTime start = request.form.get("start") end = request.form.get("end") set_config("start", start) set_config("end", end) set_config("freeze", None) # Administration name = request.form["name"] email = request.form["email"] password = request.form["password"] name_len = len(name) == 0 names = Users.query.add_columns("name", "id").filter_by(name=name).first() emails = ( Users.query.add_columns("email", "id").filter_by(email=email).first() ) pass_short = len(password) == 0 pass_long = len(password) > 128 valid_email = validators.validate_email(request.form["email"]) team_name_email_check = validators.validate_email(name) if not valid_email: errors.append("Please enter a valid email address") if names: errors.append("That user name is already taken") if team_name_email_check is True: errors.append("Your user name cannot be an email address") if emails: errors.append("That email has already been used") if pass_short: errors.append("Pick a longer password") if pass_long: errors.append("Pick a shorter password") if name_len: errors.append("Pick a longer user name") if len(errors) > 0: return render_template( "setup.html", errors=errors, name=name, email=email, password=password, state=serialize(generate_nonce()), ) admin = Admins( name=name, email=email, password=password, type="admin", hidden=True ) # Index page index = """<div class="row"> <div class="col-md-6 offset-md-3"> <img class="w-100 mx-auto d-block" style="max-width: 500px;padding: 50px;padding-top: 14vh;" src="themes/core/static/img/logo.png" /> <h3 class="text-center"> <p>CTF</p> </h3> <br> <h4 class="text-center"> <a href="admin">Click here</a> to login and setup your CTF </h4> </div> </div>""".format( request.script_root ) page = Pages(title=None, route="index", content=index, draft=False) # Visibility set_config("challenge_visibility", "private") set_config("registration_visibility", "public") set_config("score_visibility", "public") set_config("account_visibility", "public") # Verify emails set_config("verify_emails", None) set_config("mail_server", None) set_config("mail_port", None) set_config("mail_tls", None) set_config("mail_ssl", None) set_config("mail_username", None) set_config("mail_password", None) set_config("mail_useauth", None) # Set up default emails set_config("verification_email_subject", DEFAULT_VERIFICATION_EMAIL_SUBJECT) set_config("verification_email_body", DEFAULT_VERIFICATION_EMAIL_BODY) set_config( "successful_registration_email_subject", DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT, ) set_config( "successful_registration_email_body", DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY, ) set_config( "user_creation_email_subject", DEFAULT_USER_CREATION_EMAIL_SUBJECT ) set_config("user_creation_email_body", DEFAULT_USER_CREATION_EMAIL_BODY) set_config("password_reset_subject", DEFAULT_PASSWORD_RESET_SUBJECT) set_config("password_reset_body", DEFAULT_PASSWORD_RESET_BODY) set_config( "password_change_alert_subject", "Password Change Confirmation for {ctf_name}", ) set_config( "password_change_alert_body", ( "Your password for {ctf_name} has been changed.\n\n" "If you didn't request a password change you can reset your password here: {url}" ), ) set_config("setup", True) try: db.session.add(admin) db.session.commit() except IntegrityError: db.session.rollback() try: db.session.add(page) db.session.commit() except IntegrityError: db.session.rollback() login_user(admin) db.session.close() with app.app_context(): cache.clear() return redirect(url_for("views.static_html")) return render_template( "setup.html", nonce=session.get("nonce"), state=serialize(generate_nonce()), themes=config.get_themes(), ) return redirect(url_for("views.static_html"))