Example #1
0
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'))
Example #2
0
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'))
Example #3
0
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')
Example #4
0
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')
Example #5
0
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)
Example #6
0
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')
Example #7
0
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')
Example #8
0
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'))
Example #9
0
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'))
Example #10
0
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'))
Example #11
0
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)
Example #12
0
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)