def edit_challenge(id): if request.method == 'POST': form = ctforge.forms.ChallengeForm() if form.validate_on_submit(): query_handler( ('UPDATE challenges ' 'SET name = %s, description = %s, flag = %s, points = %s, ' ' active = %s, writeup = %s, writeup_template = %s ' 'WHERE id = %s'), [ form.name.data, form.description.data, form.flag.data, form.points.data, form.active.data, form.writeup.data, form.writeup_template.data, id ]) else: flash_errors(form) else: db_conn = get_db_connection() with db_conn.cursor() as cur: cur.execute('SELECT * FROM challenges WHERE id = %s', [id]) challenge = cur.fetchone() if challenge is None: flash('Invalid challenge!', 'error') else: form = ctforge.forms.ChallengeForm(**challenge) return render_template('admin/data.html', form=form, target='challenge', action='edit') return redirect(url_for('admin', tab='challenges'))
def edit_evaluation(challenge_id, user_id): # check if an evaluation has already been performed or not db_conn = db_connect() with db_conn.cursor() as cur: cur.execute(( 'SELECT U.mail AS mail, U.name, U.surname, C.name AS challenge, ' ' W.timestamp AS timestamp, W.writeup AS writeup, E.grade, E.feedback ' 'FROM writeups AS W ' 'JOIN challenges AS C ON W.challenge_id = C.id ' 'JOIN users AS U ON W.user_id = U.id ' 'LEFT JOIN challenges_evaluations AS E ' ' ON W.user_id = E.user_id AND W.challenge_id = E.challenge_id ' 'WHERE W.challenge_id = %s AND W.user_id = %s ' 'ORDER BY W.id DESC'), [challenge_id, user_id]) evaluation = cur.fetchone() if evaluation is None: flash('Writeup not submitted, cannot evaluate!', 'error') return redirect(url_for('admin', tab='evaluations')) if request.method == 'POST': form = ctforge.forms.AdminWriteupForm() if form.validate_on_submit(): if evaluation['feedback'] is None: # do a fresh insert query_handler(('INSERT INTO challenges_evaluations ' ' (user_id, challenge_id, grade, feedback) ' 'VALUES (%s, %s, %s, %s) '), [ user_id, challenge_id, form.grade.data, form.feedback.data ]) else: # only allow if not yet graded if evaluation['grade'] is None: query_handler( ('UPDATE challenges_evaluations ' 'SET grade = %s, feedback = %s, timestamp = NOW() ' 'WHERE user_id = %s AND challenge_id = %s'), [ form.grade.data, form.feedback.data, user_id, challenge_id ]) else: flash( 'Cannot modify a writeup evaluation once a grade has been set!', 'error') else: flash_errors(form) else: form = ctforge.forms.AdminWriteupForm(**evaluation) return render_template('admin/data.html', form=form, target='evaluation', action='edit') return redirect(url_for('admin', tab='evaluations'))
def add_service(): form = ctforge.forms.ServiceForm() if request.method == 'POST': if form.validate_on_submit(): query_handler( ('INSERT INTO services (name, description, active) ' 'VALUES (%s, %s, %s)'), (form.name.data, form.description.data, form.active.data)) else: flash_errors(form) return redirect(url_for('admin', tab='services')) return render_template('admin/data.html', form=form, target='service', action='add')
def add_team(): form = ctforge.forms.TeamForm() if request.method == 'POST': if form.validate_on_submit(): query_handler( ('INSERT INTO teams (ip, name, token, poc) ' 'VALUES (%s, %s, %s, %s)'), (form.ip.data, form.name.data, form.token.data, form.poc.data)) else: flash_errors(form) return redirect(url_for('admin', tab='teams')) return render_template('admin/data.html', form=form, target='team', action='add')
def login(): form = ctforge.forms.LoginForm() if request.method == 'POST': if form.validate_on_submit(): user = User.get(form.mail.data) if user is not None and bcrypt.checkpw(form.password.data, user.password): if login_user(user): return redirect(url_for('index')) else: flash('Could not log in', 'error') flash('Invalid mail or password', 'error') return redirect(url_for('login')) else: flash_errors(form) return render_template('login.html', form=form)
def add_user(): form = ctforge.forms.UserForm() if request.method == 'POST': if form.validate_on_submit(): query_handler(('INSERT INTO users (team_id, name, surname, mail, ' ' password, admin, hidden) ' 'VALUES (%s, %s, %s, %s, %s, %s, %s)'), (form.team_id.data, form.name.data, form.surname.data, form.mail.data, bcrypt.hashpw(form.password.data, bcrypt.gensalt()), form.admin.data, form.hidden.data)) else: flash_errors(form) return redirect(url_for('admin', tab='users')) return render_template('admin/data.html', form=form, target='user', action='add')
def add_challenge(): form = ctforge.forms.ChallengeForm() if request.method == 'POST': if form.validate_on_submit(): query_handler( ('INSERT INTO challenges (name, description, flag, points, ' ' active, writeup, writeup_template) ' 'VALUES (%s, %s, %s, %s, %s, %s, %s)'), [ form.name.data, form.description.data, form.flag.data, form.points.data, form.active.data, form.writeup.data, form.writeup_template.data ]) else: flash_errors(form) return redirect(url_for('admin', tab='challenges')) return render_template('admin/data.html', form=form, target='challenge', action='add')
def edit_user(id): if request.method == 'POST': form = ctforge.forms.UserForm() if form.validate_on_submit(): if form.password.data: # update the password query_handler( ('UPDATE users ' 'SET team_id = %s, name = %s, surname = %s, ' ' mail = %s, password = %s, admin = %s, hidden = %s ' 'WHERE id = %s'), (form.team_id.data, form.name.data, form.surname.data, form.mail.data, bcrypt.hashpw(form.password.data, bcrypt.gensalt()), form.admin.data, form.hidden.data, id)) else: query_handler( ('UPDATE users ' 'SET team_id = %s, name = %s, surname = %s, ' ' mail = %s, admin = %s, hidden = %s ' 'WHERE id = %s'), (form.team_id.data, form.name.data, form.surname.data, form.mail.data, form.admin.data, form.hidden.data, id)) else: flash_errors(form) else: db_conn = get_db_connection() with db_conn.cursor() as cur: cur.execute( ('SELECT id, team_id, name, surname, mail, admin, hidden ' 'FROM users ' 'WHERE id = %s'), [id]) user = cur.fetchone() if user is None: flash('Invalid user!', 'error') else: form = ctforge.forms.UserForm(**user) return render_template('admin/data.html', form=form, target='user', action='edit') return redirect(url_for('admin', tab='users'))
def edit_service(id): if request.method == 'POST': form = ctforge.forms.ServiceForm() if form.validate_on_submit(): query_handler(( 'UPDATE services SET name = %s, description = %s, active = %s ' 'WHERE id = %s'), (form.name.data, form.description.data, form.active.data, id)) else: flash_errors(form) else: db_conn = get_db_connection() with db_conn.cursor() as cur: cur.execute('SELECT * FROM services WHERE id = %s', [id]) service = cur.fetchone() if service is None: flash('Invalid service!', 'error') else: form = ctforge.forms.ServiceForm(**service) return render_template('admin/data.html', form=form, target='service', action='edit') return redirect(url_for('admin', tab='services'))
def edit_team(id): if request.method == 'POST': form = ctforge.forms.TeamForm() if form.validate_on_submit(): query_handler( ('UPDATE teams SET ip = %s, name = %s, token = %s, poc = %s ' 'WHERE id = %s'), (form.ip.data, form.name.data, form.token.data, form.poc.data, id)) else: flash_errors(form) else: db_conn = get_db_connection() with db_conn.cursor() as cur: cur.execute('SELECT * FROM teams WHERE id = %s', [id]) team = cur.fetchone() if team is None: flash('Invalid team!', 'error') else: form = ctforge.forms.TeamForm(**team) return render_template('admin/data.html', form=form, target='team', action='edit') return redirect(url_for('admin', tab='teams'))
def challenge(name): """Display information about a challenge plus the flag submission form and the writeup.""" db_conn = db_connect() cur = db_conn.cursor() # get challenge data if the challenge exists cur.execute('SELECT * FROM challenges WHERE name = %s', [name]) challenge = cur.fetchone() # if the challenge is not valid abort if challenge is None: cur.close() abort(404) # check if the current user already solved the challenge cur.execute(('SELECT * FROM challenge_attacks ' 'WHERE user_id = %s AND challenge_id = %s'), [current_user.id, challenge['id']]) solved = cur.fetchone() is not None # get the list of all the writeups submitted by this user for this challenge cur.execute(('SELECT id, timestamp FROM writeups ' 'WHERE user_id = %s AND challenge_id = %s ' 'ORDER BY id DESC'), [current_user.id, challenge['id']]) writeups = cur.fetchall() # get the evaluation for this challenge evaluation = None if writeups: cur.execute( ('SELECT feedback, grade, timestamp FROM challenges_evaluations ' 'WHERE user_id = %s AND challenge_id = %s '), [current_user.id, challenge['id']]) evaluation = cur.fetchone() graded = evaluation is not None and evaluation['grade'] is not None # retrieve the writeup form, if any writeup_form = ctforge.forms.ChallengeWriteupForm( writeup=challenge['writeup_template']) # retrive the flag form flag_form = ctforge.forms.ChallengeFlagForm() # accept POST requests only if the challenge is active if request.method == 'POST' and challenge['active']: # process the two mutually exclusive forms writeup_data = request.form.get('writeup') flag = request.form.get('flag') if writeup_data is not None: # only allow writeup submission if writeup support is enabled for this chal if challenge['writeup'] and writeup_form.validate_on_submit(): if graded: # writeup already submitted, resubmission allowed only if there's no grade flash( 'Your submission has already been graded, you cannot modify it', 'error') else: writeup_data = writeup_form.writeup.data try: # save this writeup into the db cur.execute(( 'INSERT INTO writeups (user_id, challenge_id, writeup) ' 'VALUES (%s, %s, %s) RETURNING id'), [ current_user.id, challenge['id'], writeup_data ]) writeup_id = cur.fetchone()['id'] cur.close() db_conn.commit() flash('Writeup added', 'success') except psycopg2.Error as e: db_conn.rollback() error_msg = 'Unknown database error: {}'.format(e) flash(error_msg, 'error') app.logger.error(error_msg) else: flash_errors(writeup_form) else: if cur is None: cur = db_conn.cursor() if flag is not None and flag_form.validate_on_submit(): flag = flag_form.flag.data if flag == challenge['flag']: try: # save this attack into the db cur.execute(( 'INSERT INTO challenge_attacks (user_id, challenge_id) ' 'VALUES (%s, %s)'), [current_user.id, challenge['id']]) cur.close() db_conn.commit() flash('Flag accepted!', 'success') except psycopg2.IntegrityError: # this exception is raised not only on duplicated entry, # but also when key constraint fails db_conn.rollback() flash('You already solved this challenge') except psycopg2.Error as e: db_conn.rollback() error_msg = 'Unknown database error: {}'.format(e) flash(error_msg, 'error') app.logger.error(error_msg) else: flash('Invalid flag', 'error') else: flash_errors(flag_form) # close the pending connection to the database db_conn.close() return redirect(url_for('challenge', name=challenge['name'])) db_conn.close() return render_template('challenge.html', flag_form=flag_form, writeup_form=writeup_form, challenge=challenge, evaluation=evaluation, solved=solved, graded=graded, writeups=writeups)
def submit(): """Flag submitter service.""" team_token = None # get the token associated with user's team if current_user.is_authenticated: db_conn = get_db_connection() with db_conn.cursor() as cur: cur.execute('SELECT token FROM teams WHERE id = %s', [current_user.team_id]) res = cur.fetchone() team_token = res['token'] if res is not None else None # initialize the flag form form = ctforge.forms.ServiceFlagForm(csrf_enabled=False) if request.method == 'POST': # process the form if form.validate_on_submit(): team_token = form.team_token.data flag = form.flag.data try: db_conn = db_connect() db_conn.autocommit = False cur = db_conn.cursor() # get the team associated with the retrieved token cur.execute('SELECT id FROM teams WHERE token = %s', [team_token]) res = cur.fetchone() if res is None: raise ctforge.exceptions.InvalidToken() team_id = res['id'] # get the flag that the user is trying to submit, if valid # (i.e. active and not one of the flags of his team) cur.execute(('SELECT service_id FROM active_flags ' 'WHERE flag = %s AND team_id != %s'), [flag, team_id]) res = cur.fetchone() if res is None: raise ctforge.exceptions.InvalidFlag() service_id = res['service_id'] # check whether the team's service is well-functioning or not cur.execute(('SELECT I.successful, I.timestamp ' 'FROM active_flags AS A ' 'JOIN integrity_checks AS I ' 'ON A.flag = I.flag ' 'WHERE A.team_id = %s AND A.service_id = %s ' 'ORDER BY I.timestamp DESC LIMIT 1'), [team_id, service_id]) res = cur.fetchone() if res is None or res['successful'] != 1: raise ctforge.exceptions.ServiceCorrupted() # store the attack in the database cur.execute(('INSERT INTO service_attacks (team_id, flag) ' 'VALUES (%s, %s) '), [team_id, flag]) db_conn.commit() flash('Flag accepted!', 'success') except psycopg2.IntegrityError: # this exception is raised not only on duplicated entry, but also # when key constraint fails db_conn.rollback() flash('Duplicated flag!', 'error') except psycopg2.Error as e: db_conn.rollback() error_msg = 'Unknown database error: {}'.format(e) flash(error_msg, 'error') app.logger.error(error_msg) except ctforge.exceptions.InvalidToken: db_conn.rollback() flash('The provided token does not match any team!', 'error') except ctforge.exceptions.InvalidFlag: db_conn.rollback() flash('The submitted flag is invalid!', 'error') except ctforge.exceptions.ServiceCorrupted: db_conn.rollback() flash( 'Your service is corrupted, fix it before submitting flags!', 'error') except Exception as e: # this should never occur, but we keep it for safety reasons error_msg = 'Unknown error: {}'.format(e) flash(error_msg, 'error') app.logger.error(error_msg) finally: cur.close() db_conn.close() else: flash_errors(form) return render_template('submit.html', form=form, team_token=team_token)