示例#1
0
def db_create_schema():
    db_conn = database.db_connect('postgres')
    db_conn.autocommit = True
    with db_conn.cursor() as cur:
        cur.execute('DROP DATABASE IF EXISTS {}'.format(app.config['DB_NAME']))
        cur.execute("CREATE DATABASE {} WITH ENCODING 'UTF8'".format(app.config['DB_NAME']))
    db_conn.close()

    db_conn = database.db_connect()
    with db_conn.cursor() as cur:
        cur.execute(pkgutil.get_data('ctforge', 'db/schema.sql'))
    db_conn.commit()
    db_conn.close()
示例#2
0
def db_add_user(name, surname, mail, password, admin=False, hidden=False, team_id=None):
    db_conn = database.db_connect()
    with db_conn.cursor() as cur:
        cur.execute((
            'INSERT INTO users (team_id, name, surname, mail, password, admin, hidden) '
            'VALUES (%s, %s, %s, %s, %s, %s, %s)'),
            [team_id, name, surname, mail, bcrypt.hashpw(password, bcrypt.gensalt()),
             admin, hidden])
    db_conn.commit()
    db_conn.close()
示例#3
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'))
示例#4
0
def main():
    global logger, db_conn

    # parse command line options, the round parameter is required
    parser = argparse.ArgumentParser(description='Flag dispatcher and checker')
    parser.add_argument('--advance',
                        action='store_true',
                        default=False,
                        help="Advance the current round")
    parser.add_argument('--dispatch',
                        action='store_true',
                        default=False,
                        help="Dispatch new flags to all the virtual machines")
    parser.add_argument('--check',
                        action='store_true',
                        default=False,
                        help="Check the integrity of the services")
    parser.add_argument('-n',
                        dest='num_workers',
                        type=int,
                        default=1,
                        help="Number of concurrent workers (default 1)")
    parser.add_argument(
        '-t',
        dest='timeout',
        type=int,
        default=10,
        help="Seconds to wait before killing a spawned script (default 10)")
    parser.add_argument('-v',
                        dest='verbose',
                        action='store_true',
                        default=False,
                        help="Set logging level to debug")
    args = parser.parse_args()
    if not any([args.advance, args.dispatch, args.check]):
        sys.stderr.write("At least one action is required, aborting.\n")
        abort()

    # register the killer handler
    signal.signal(signal.SIGINT, interrupt)

    # set variables
    n_workers = args.num_workers
    log_level = logging.DEBUG if args.verbose else logging.INFO

    # set logging
    logger.setLevel(log_level)
    formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
    fh = logging.FileHandler(config['BOT_LOG_FILE'])
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    # if the verbose mode is selected, log also on the console
    if args.verbose:
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        logger.addHandler(ch)

    # start the global db connection
    db_conn = database.db_connect(logger=logger)

    # retrieve the list of teams and services
    teams, services = get_teams_services()
    if args.advance:
        # advance the round
        advance_round(teams, services)
    if args.check or args.dispatch:
        # fill the queue of tasks, choosing the team order randomly :)
        for service in services:
            for team in random.sample(teams, len(teams)):
                tasks.put_nowait((team, service))

        # create the list of workers
        workers = []
        for i in range(n_workers):
            worker = Worker(i, args.dispatch, args.check, args.timeout)
            workers.append(worker)
            worker.start()

        # wait responsively until the queue is empty or an event is thrown
        while not tasks.empty():
            Worker.killing_time.wait(1)

        # join all workers
        for worker in workers:
            worker.join()

    # close the connection to the db
    db_conn.close()

    # exit gracefully
    sys.exit(0)
示例#5
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)
示例#6
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)
示例#7
0
def db_create_procedures():
    db_conn = database.db_connect()
    with db_conn.cursor() as cur:
        cur.execute(pkgutil.get_data('ctforge', 'db/procedures.sql'))
    db_conn.commit()
    db_conn.close()