Example #1
0
def test_get_config_and_set_config():
    """Does get_config and set_config work properly"""
    app = create_ctfd()
    with app.app_context():
        assert get_config('setup') == True
        config = set_config('TEST_CONFIG_ENTRY', 'test_config_entry')
        assert config.value == 'test_config_entry'
        assert get_config('TEST_CONFIG_ENTRY') == 'test_config_entry'
Example #2
0
def attempts():
    chals = Challenges.query.add_columns('id').all()
    json = {'maxattempts':[]}
    for chal, chalid in chals:
        fails = WrongKeys.query.filter_by(team=session['id'], chal=chalid).count()
        if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
            json['maxattempts'].append({'chalid':chalid})
    return jsonify(json)
Example #3
0
def attempts():
    if not utils.user_can_view_challenges():
        return redirect(url_for('auth.login', next=request.path))
    chals = Challenges.query.add_columns('id').all()
    json = {'maxattempts': []}
    for chal, chalid in chals:
        fails = WrongKeys.query.filter_by(teamid=session['id'], chalid=chalid).count()
        if fails >= int(utils.get_config("max_tries")) and int(utils.get_config("max_tries")) > 0:
            json['maxattempts'].append({'chalid': chalid})
    return jsonify(json)
Example #4
0
def chal(chalid):
    if not ctftime():
        return redirect('/challenges')
    if authed():
        fails = WrongKeys.query.filter_by(team=session['id'],chal=chalid).count()
        logger = logging.getLogger('keys')
        data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), request.form['key'].encode('utf-8'), get_kpm(session['id']))
        print "[{0}] {1} submitted {2} with kpm {3}".format(*data)
        if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
            return "4" #too many tries on this challenge
        if get_kpm(session['id']) > 10:
            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.warn("[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(*data))
            return "3" # Submitting too fast
        solves = Solves.query.filter_by(teamid=session['id'], chalid=chalid).first()
        if not solves:
            keys = Keys.query.filter_by(chal=chalid).all()
            key = request.form['key'].strip().lower()
            for x in keys:
                if x.key_type == 0: #static key
                    if x.flag.strip().lower() == key:
                        solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
                        return "1" # key was correct
                elif x.key_type == 1: #regex
                    res = re.match(str(x), key, re.IGNORECASE)
                    if res and res.group() == key:
                        solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
                        return "1" # key was correct

            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
            return '0' # key was wrong
        else:
            logger.info("{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format(*data))
            return "2" # challenge was already solved
    else:
        return "-1"
Example #5
0
def admin_pages(route):
    if request.method == 'GET' and request.args.get('mode') == 'create':
        return render_template('admin/editor.html')
    if route and request.method == 'GET':
        page = Pages.query.filter_by(route=route).first()
        return render_template('admin/editor.html', page=page)
    if route and request.method == 'POST':
        page = Pages.query.filter_by(route=route).first()
        errors = []
        html = request.form['html']
        route = request.form['route']
        if not route:
            errors.append('Missing URL route')
        if errors:
            page = Pages(html, "")
            return render_template('/admin/editor.html', page=page)
        if page:
            page.route = route
            page.html = html
            db.session.commit()
            return redirect(url_for('admin.admin_pages'))
        page = Pages(route, html)
        db.session.add(page)
        db.session.commit()
        return redirect(url_for('admin.admin_pages'))
    pages = Pages.query.all()
    return render_template('admin/pages.html', routes=pages, css=get_config('css'))
Example #6
0
def topteams(count):
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    try:
        count = int(count)
    except:
        count = 10
    if count > 20 or count < 0:
        count = 10

    json = {'scores':{}}

    score = db.func.sum(Challenges.value).label('score')
    quickest = db.func.max(Solves.date).label('quickest')
    teams = db.session.query(Solves.teamid, Teams.name, score)\
        .join(Teams)\
        .join(Challenges)\
        .filter(Teams.banned == None)\
        .group_by(Solves.teamid).order_by(score.desc(), quickest)\
        .limit(count)

    for team in teams:
        solves = Solves.query.filter_by(teamid=team.teamid).all()
        json['scores'][team.name] = []
        for x in solves:
            json['scores'][team.name].append({
                'id': x.teamid,
                'chal': x.chalid,
                'team': x.teamid,
                'value': x.chal.value,
                'time': unix_time(x.date)
            })

    return jsonify(json)
Example #7
0
def scores():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    score = db.func.sum(Challenges.value).label('score')
    scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \
        .join(Teams) \
        .join(Challenges) \
        .filter(Teams.banned == None) \
        .group_by(Solves.teamid)

    awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'), db.func.sum(Awards.value).label('score'), Awards.date.label('date'))\
        .filter(Teams.id==Awards.teamid)\
        .group_by(Teams.id)

    results = union_all(scores, awards)

    standings = db.session.query(results.columns.teamid, results.columns.name, db.func.sum(results.columns.score).label('score'))\
        .group_by(results.columns.teamid)\
        .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date))\
        .all()

    db.session.close()
    json = {'standings':[]}
    for i, x in enumerate(standings):
        json['standings'].append({'pos':i+1, 'id':x.teamid, 'team':x.name,'score':int(x.score)})
    return jsonify(json)
Example #8
0
def topteams(count):
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    try:
        count = int(count)
    except:
        count = 10
    if count > 20 or count < 0:
        count = 10

    json = {'scores':{}}
    standings = get_standings(count=count)

    for team in standings:
        solves = Solves.query.filter_by(teamid=team.teamid).all()
        awards = Awards.query.filter_by(teamid=team.teamid).all()
        json['scores'][team.name] = []
        scores = []
        for x in solves:
            json['scores'][team.name].append({
                'chal': x.chalid,
                'team': x.teamid,
                'value': x.chal.value,
                'time': unix_time(x.date)
            })
        for award in awards:
            json['scores'][team.name].append({
                'chal': None,
                'team': award.teamid,
                'value': award.value,
                'time': unix_time(award.date)
            })
        json['scores'][team.name] = sorted(json['scores'][team.name], key=lambda k: k['time'])
    return jsonify(json)
Example #9
0
def scores():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    standings = get_standings()
    json = {'standings':[]}
    for i, x in enumerate(standings):
        json['standings'].append({'pos':i+1, 'id':x.teamid, 'team':x.name,'score':int(x.score)})
    return jsonify(json)
Example #10
0
def challenges_view():
    errors = []
    start = utils.get_config('start') or 0
    end = utils.get_config('end') or 0
    if not utils.is_admin():  # User is not an admin
        if not utils.ctftime():
            # It is not CTF time
            if utils.view_after_ctf():  # But we are allowed to view after the CTF ends
                pass
            else:  # We are NOT allowed to view after the CTF ends
                if utils.get_config('start') and not utils.ctf_started():
                    errors.append('{} has not started yet'.format(utils.ctf_name()))
                if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf():
                    errors.append('{} has ended'.format(utils.ctf_name()))
                return render_template('chals.html', errors=errors, start=int(start), end=int(end))
        if utils.get_config('verify_emails') and not utils.is_verified():  # User is not confirmed
            return redirect(url_for('auth.confirm_user'))
    if utils.user_can_view_challenges():  # Do we allow unauthenticated users?
        if utils.get_config('start') and not utils.ctf_started():
            errors.append('{} has not started yet'.format(utils.ctf_name()))
        if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf():
            errors.append('{} has ended'.format(utils.ctf_name()))
        return render_template('chals.html', errors=errors, start=int(start), end=int(end))
    else:
        return redirect(url_for('auth.login', next='challenges'))
Example #11
0
def register():
    if not can_register():
        return redirect(url_for('auth.login'))
    if request.method == 'POST':
        errors = []
        name = request.form['name']
        email = request.form['email']
        schoolCode = request.form['schoolCode']
        password = request.form['password']

        name_len = len(name) == 0
        names = Teams.query.add_columns('name', 'id').filter_by(name=name).first()
        emails = Teams.query.add_columns('email', 'id').filter_by(email=email).first()
        pass_short = len(password) == 0
        pass_long = len(password) > 128
        valid_email = re.match("[^@]+@[^@]+\.[^@]+", request.form['email'])

        if not valid_email:
            errors.append("That email doesn't look right")
        if names:
            errors.append('That team name is already taken')
        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 team name')

        if len(errors) > 0:
            return render_template('register.html', errors=errors, name=request.form['name'], email=request.form['email'], schoolCode=request.form['schoolCode'], password=request.form['password'])
        else:
            with app.app_context():
                team = Teams(name, email.lower(), schoolCode, password)
                db.session.add(team)
                db.session.commit()
                db.session.flush()

                session['username'] = team.name
                session['id'] = team.id
                session['admin'] = team.admin
                session['nonce'] = sha512(os.urandom(10))

                if mailserver() and get_config('verify_emails'):
                    verify_email(team.email)
                else:
                    if mailserver():
                        sendmail(request.form['email'], "You've successfully registered for {}".format(get_config('ctf_name')))

        db.session.close()

        logger = logging.getLogger('regs')
        logger.warn("[{0}] {1} registered with {2}".format(time.strftime("%m/%d/%Y %X"), request.form['name'].encode('utf-8'), request.form['email'].encode('utf-8')))
        return redirect(url_for('challenges.challenges_view'))
    else:
        return render_template('register.html')
Example #12
0
def scoreboard_view():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    score = db.func.sum(Challenges.value).label('score')
    quickest = db.func.max(Solves.date).label('quickest')
    teams = db.session.query(Solves.teamid, Teams.name, score)\
        .join(Teams)\
        .join(Challenges)\
        .filter(Teams.banned == None)\
        .group_by(Solves.teamid).order_by(score.desc(), quickest)
    db.session.close()
    return render_template('scoreboard.html', teams=teams)
Example #13
0
def topteams(count):
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    try:
        count = int(count)
    except:
        count = 10
    if count > 20 or count < 0:
        count = 10

    json = {'scores':{}}

    score = db.func.sum(Challenges.value).label('score')
    scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \
        .join(Teams) \
        .join(Challenges) \
        .filter(Teams.banned == None) \
        .group_by(Solves.teamid)

    awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'),
                              db.func.sum(Awards.value).label('score'), Awards.date.label('date')) \
        .filter(Teams.id == Awards.teamid) \
        .group_by(Teams.id)

    results = union_all(scores, awards)

    standings = db.session.query(results.columns.teamid, results.columns.name,
                                 db.func.sum(results.columns.score).label('score')) \
        .group_by(results.columns.teamid) \
        .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date)) \
        .limit(count).all()

    for team in standings:
        solves = Solves.query.filter_by(teamid=team.teamid).all()
        awards = Awards.query.filter_by(teamid=team.teamid).all()
        json['scores'][team.name] = []
        scores = []
        for x in solves:
            json['scores'][team.name].append({
                'chal': x.chalid,
                'team': x.teamid,
                'value': x.chal.value,
                'time': unix_time(x.date)
            })
        for award in awards:
            json['scores'][team.name].append({
                'chal': None,
                'team': award.teamid,
                'value': award.value,
                'time': unix_time(award.date)
            })
        json['scores'][team.name] = sorted(json['scores'][team.name], key=lambda k: k['time'])
    return jsonify(json)
Example #14
0
def challenges_view():
    if not is_admin():
        if not ctftime():
            if view_after_ctf():
                pass
            else:
                return redirect(url_for('views.static_html'))
        if get_config('verify_emails') and not is_verified():
            return redirect(url_for('auth.confirm_user'))
    if can_view_challenges():
        return render_template('chals.html', ctftime=ctftime())
    else:
        return redirect(url_for('auth.login', next='challenges'))
Example #15
0
def teams(page):
    page = abs(int(page))
    results_per_page = 50
    page_start = results_per_page * ( page - 1 )
    page_end = results_per_page * ( page - 1 ) + results_per_page

    if get_config('verify_emails'):
        teams = Teams.query.filter_by(verified=True).slice(page_start, page_end).all()
    else:
        teams = Teams.query.slice(page_start, page_end).all()
    count = len(teams)
    pages = int(count / results_per_page) + (count % results_per_page > 0)
    return render_template('teams.html', teams=teams, team_pages=pages, curr_page=page)
Example #16
0
def test_sendmail_with_smtp(mock_smtp):
    """Does sendmail work properly with simple SMTP mail servers"""
    from email.mime.text import MIMEText
    app = create_ctfd()
    with app.app_context():
        set_config('mail_server', 'localhost')
        set_config('mail_port', 25)
        set_config('mail_username', 'username')
        set_config('mail_password', 'password')

        from_addr = get_config('mailfrom_addr') or app.config.get('MAILFROM_ADDR')
        to_addr = '*****@*****.**'
        msg = 'this is a test'

        sendmail(to_addr, msg)

        ctf_name = get_config('ctf_name')
        email_msg = MIMEText(msg)
        email_msg['Subject'] = "Message from {0}".format(ctf_name)
        email_msg['From'] = from_addr
        email_msg['To'] = to_addr

        mock_smtp.return_value.sendmail.assert_called_once_with(from_addr, [to_addr], email_msg.as_string())
Example #17
0
def solves(teamid=None):
    solves = None
    awards = None
    if teamid is None:
        if utils.is_admin():
            solves = Solves.query.filter_by(teamid=session['id']).all()
        elif utils.user_can_view_challenges():
            if utils.authed():
                solves = Solves.query.join(Teams, Solves.teamid == Teams.id).filter(Solves.teamid == session['id'], Teams.banned == False).all()
            else:
                return jsonify({'solves': []})
        else:
            return redirect(url_for('auth.login', next='solves'))
    else:
        solves = Solves.query.filter_by(teamid=teamid)
        awards = Awards.query.filter_by(teamid=teamid)

        freeze = utils.get_config('freeze')
        if freeze:
            freeze = utils.unix_time_to_utc(freeze)
            if teamid != session.get('id'):
                solves = solves.filter(Solves.date < freeze)
                awards = awards.filter(Awards.date < freeze)

        solves = solves.all()
        awards = awards.all()
    db.session.close()
    json = {'solves': []}
    for solve in solves:
        json['solves'].append({
            'chal': solve.chal.name,
            'chalid': solve.chalid,
            'team': solve.teamid,
            'value': solve.chal.value,
            'category': solve.chal.category,
            'time': utils.unix_time(solve.date)
        })
    if awards:
        for award in awards:
            json['solves'].append({
                'chal': award.name,
                'chalid': None,
                'team': award.teamid,
                'value': award.value,
                'category': award.category or "Award",
                'time': utils.unix_time(award.date)
            })
    json['solves'].sort(key=lambda k: k['time'])
    return jsonify(json)
Example #18
0
def team(teamid):
    if utils.get_config('view_scoreboard_if_utils.authed') and not utils.authed():
        return redirect(url_for('auth.login', next=request.path))
    errors = []
    freeze = utils.get_config('freeze')
    user = Teams.query.filter_by(id=teamid).first_or_404()
    solves = Solves.query.filter_by(teamid=teamid)
    awards = Awards.query.filter_by(teamid=teamid)

    place = user.place()
    score = user.score()

    if freeze:
        freeze = utils.unix_time_to_utc(freeze)
        if teamid != session.get('id'):
            solves = solves.filter(Solves.date < freeze)
            awards = awards.filter(Awards.date < freeze)

    solves = solves.all()
    awards = awards.all()

    db.session.close()

    if utils.hide_scores() and teamid != session.get('id'):
        errors.append('Scores are currently hidden')

    if errors:
        return render_template('team.html', team=user, errors=errors)

    if request.method == 'GET':
        return render_template('team.html', solves=solves, awards=awards, team=user, score=score, place=place, score_frozen=utils.is_scoreboard_frozen())
    elif request.method == 'POST':
        json = {'solves': []}
        for x in solves:
            json['solves'].append({'id': x.id, 'chal': x.chalid, 'team': x.teamid})
        return jsonify(json)
Example #19
0
    def get_source(self, environment, template):
        # Check if the template has been overriden
        if template in self.overriden_templates:
            return self.overriden_templates[template], template, True

        # Check if the template requested is for the admin panel
        if template.startswith('admin/'):
            template = template.lstrip('admin/')
            template = "/".join(['admin', 'templates', template])
            return super(ThemeLoader, self).get_source(environment, template)

        # Load regular theme data
        theme = utils.get_config('ctf_theme')
        template = "/".join([theme, 'templates', template])
        return super(ThemeLoader, self).get_source(environment, template)
Example #20
0
def scores():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    score = db.func.sum(Challenges.value).label('score')
    quickest = db.func.max(Solves.date).label('quickest')
    teams = db.session.query(Solves.teamid, Teams.name, score)\
        .join(Teams)\
        .join(Challenges)\
        .filter(Teams.banned == None)\
        .group_by(Solves.teamid).order_by(score.desc(), quickest)
    db.session.close()
    json = {'standings':[]}
    for i, x in enumerate(teams):
        json['standings'].append({'pos':i+1, 'id':x.teamid, 'team':x.name,'score':int(x.score)})
    return jsonify(json)
Example #21
0
def team(teamid):
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    user = Teams.query.filter_by(id=teamid).first()
    solves = Solves.query.filter_by(teamid=teamid).all()
    score = user.score()
    place = user.place()
    db.session.close()

    if request.method == 'GET':
        return render_template('team.html', solves=solves, team=user, score=score, place=place)
    elif request.method == 'POST':
        json = {'solves':[]}
        for x in solves:
            json['solves'].append({'id':x.id, 'chal':x.chalid, 'team':x.teamid})
        return jsonify(json)
Example #22
0
def confirm_user(data=None):
    if not utils.get_config('verify_emails'):
        # If the CTF doesn't care about confirming email addresses then redierct to challenges
        return redirect(url_for('challenges.challenges_view'))

    # User is confirming email account
    if data and request.method == "GET":
        try:
            s = Signer(app.config['SECRET_KEY'])
            email = s.unsign(urllib.unquote_plus(data.decode('base64')))
        except BadSignature:
            return render_template('confirm.html', errors=['Your confirmation link seems wrong'])
        except:
            return render_template('confirm.html', errors=['Your link appears broken, please try again.'])
        team = Teams.query.filter_by(email=email).first_or_404()
        team.verified = True
        db.session.commit()
        logger = logging.getLogger('regs')
        logger.warn("[{0}] {1} confirmed {2}".format(time.strftime("%m/%d/%Y %X"), team.name.encode('utf-8'), team.email.encode('utf-8')))
        db.session.close()
        if utils.authed():
            return redirect(url_for('challenges.challenges_view'))
        return redirect(url_for('auth.login'))

    # User is trying to start or restart the confirmation flow
    if not utils.authed():
        return redirect(url_for('auth.login'))

    team = Teams.query.filter_by(id=session['id']).first_or_404()

    if data is None:
        if request.method == "POST":
            # User wants to resend their confirmation email
            if team.verified:
                return redirect(url_for('views.profile'))
            else:
                utils.verify_email(team.email)
            return render_template('confirm.html', team=team, infos=['Your confirmation email has been resent!'])
        elif request.method == "GET":
            # User has been directed to the confirm page
            team = Teams.query.filter_by(id=session['id']).first_or_404()
            if team.verified:
                # If user is already verified, redirect to their profile
                return redirect(url_for('views.profile'))
            return render_template('confirm.html', team=team)
Example #23
0
def confirm_user(data=None):
    if not get_config('verify_emails'):
        return redirect(url_for('challenges.challenges_view'))
    if data and request.method == "GET":  ## User is confirming email account
        try:
            s = Signer(app.config['SECRET_KEY'])
            email = s.unsign(data.decode('base64'))
        except BadSignature:
            return render_template('confirm.html', errors=['Your confirmation link seems wrong'])
        team = Teams.query.filter_by(email=email).first()
        team.verified = True
        db.session.commit()
        db.session.close()
        if authed():
            return redirect(url_for('challenges.challenges_view'))
        return redirect(url_for('auth.login'))
    if not data and request.method == "GET": ## User has been directed to the confirm page because his account is not verified
        team = Teams.query.filter_by(id=session['id']).first()
        if team.verified:
            return redirect(url_for('views.profile'))
        return render_template('confirm.html', team=team)
Example #24
0
def scoreboard_view():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    score = db.func.sum(Challenges.value).label('score')
    scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \
        .join(Teams) \
        .join(Challenges) \
        .filter(Teams.banned == None) \
        .group_by(Solves.teamid)

    awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'),
                              db.func.sum(Awards.value).label('score'), Awards.date.label('date')) \
        .filter(Teams.id == Awards.teamid) \
        .group_by(Teams.id)

    results = union_all(scores, awards)

    standings = db.session.query(results.columns.teamid, results.columns.name,
                                 db.func.sum(results.columns.score).label('score')) \
        .group_by(results.columns.teamid) \
        .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date)) \
        .all()
    db.session.close()
    return render_template('scoreboard.html', teams=standings)
Example #25
0
def admin_config():
    if request.method == "POST":
        start = None
        end = None
        if request.form.get('start'):
            start = int(request.form['start'])
        if request.form.get('end'):
            end = int(request.form['end'])

        try:
            view_challenges_unregistered = bool(
                request.form.get('view_challenges_unregistered', None))
            view_scoreboard_if_authed = bool(
                request.form.get('view_scoreboard_if_authed', None))
            prevent_registration = bool(
                request.form.get('prevent_registration', None))
            prevent_name_change = bool(
                request.form.get('prevent_name_change', None))
            view_after_ctf = bool(request.form.get('view_after_ctf', None))
            verify_emails = bool(request.form.get('verify_emails', None))
            mail_tls = bool(request.form.get('mail_tls', None))
            mail_ssl = bool(request.form.get('mail_ssl', None))
        except (ValueError, TypeError):
            view_challenges_unregistered = None
            view_scoreboard_if_authed = None
            prevent_registration = None
            prevent_name_change = None
            view_after_ctf = None
            verify_emails = None
            mail_tls = None
            mail_ssl = None
        finally:
            view_challenges_unregistered = set_config(
                'view_challenges_unregistered', view_challenges_unregistered)
            view_scoreboard_if_authed = set_config('view_scoreboard_if_authed',
                                                   view_scoreboard_if_authed)
            prevent_registration = set_config('prevent_registration',
                                              prevent_registration)
            prevent_name_change = set_config('prevent_name_change',
                                             prevent_name_change)
            view_after_ctf = set_config('view_after_ctf', view_after_ctf)
            verify_emails = set_config('verify_emails', verify_emails)
            mail_tls = set_config('mail_tls', mail_tls)
            mail_ssl = set_config('mail_ssl', mail_ssl)

        mail_server = set_config("mail_server",
                                 request.form.get('mail_server', None))
        mail_port = set_config("mail_port",
                               request.form.get('mail_port', None))

        mail_username = set_config("mail_username",
                                   request.form.get('mail_username', None))
        mail_password = set_config("mail_password",
                                   request.form.get('mail_password', None))

        ctf_name = set_config("ctf_name", request.form.get('ctf_name', None))
        ctf_theme = set_config("ctf_theme",
                               request.form.get('ctf_theme', None))

        mailfrom_addr = set_config("mailfrom_addr",
                                   request.form.get('mailfrom_addr', None))
        mg_base_url = set_config("mg_base_url",
                                 request.form.get('mg_base_url', None))
        mg_api_key = set_config("mg_api_key",
                                request.form.get('mg_api_key', None))

        max_tries = set_config("max_tries",
                               request.form.get('max_tries', None))

        db_start = Config.query.filter_by(key='start').first()
        db_start.value = start

        db_end = Config.query.filter_by(key='end').first()
        db_end.value = end

        db.session.add(db_start)
        db.session.add(db_end)

        db.session.commit()
        db.session.close()
        with app.app_context():
            cache.clear()
        return redirect(url_for('admin.admin_config'))

    with app.app_context():
        cache.clear()
    ctf_name = get_config('ctf_name')
    ctf_theme = get_config('ctf_theme')
    max_tries = get_config('max_tries')

    mail_server = get_config('mail_server')
    mail_port = get_config('mail_port')
    mail_username = get_config('mail_username')
    mail_password = get_config('mail_password')

    mailfrom_addr = get_config('mailfrom_addr')
    mg_api_key = get_config('mg_api_key')
    mg_base_url = get_config('mg_base_url')
    if not max_tries:
        set_config('max_tries', 0)
        max_tries = 0

    view_after_ctf = get_config('view_after_ctf')
    start = get_config('start')
    end = get_config('end')

    mail_tls = get_config('mail_tls')
    mail_ssl = get_config('mail_ssl')

    view_challenges_unregistered = get_config('view_challenges_unregistered')
    view_scoreboard_if_authed = get_config('view_scoreboard_if_authed')
    prevent_registration = get_config('prevent_registration')
    prevent_name_change = get_config('prevent_name_change')
    verify_emails = get_config('verify_emails')

    db.session.commit()
    db.session.close()

    themes = get_themes()
    themes.remove(ctf_theme)

    return render_template(
        'admin/config.html',
        ctf_name=ctf_name,
        ctf_theme_config=ctf_theme,
        start=start,
        end=end,
        max_tries=max_tries,
        mail_server=mail_server,
        mail_port=mail_port,
        mail_username=mail_username,
        mail_password=mail_password,
        mail_tls=mail_tls,
        mail_ssl=mail_ssl,
        view_challenges_unregistered=view_challenges_unregistered,
        view_scoreboard_if_authed=view_scoreboard_if_authed,
        prevent_registration=prevent_registration,
        mailfrom_addr=mailfrom_addr,
        mg_base_url=mg_base_url,
        mg_api_key=mg_api_key,
        prevent_name_change=prevent_name_change,
        verify_emails=verify_emails,
        view_after_ctf=view_after_ctf,
        themes=themes)
Example #26
0
 def _check_registration_visibility(*args, **kwargs):
     v = get_config('registration_visibility')
     if v == 'public':
         return f(*args, **kwargs)
     elif v == 'private':
         abort(404)
Example #27
0
def ctf_name():
    name = get_config("ctf_name")
    return name if name else "CTFd"
Example #28
0
def admin_config():
    if request.method == "POST":
        try:
            start = int(request.form['start'])
            end = int(request.form['end'])
        except (ValueError, TypeError):
            start = None
            end = None

        try:
            view_challenges_unregistered = bool(request.form.get('view_challenges_unregistered', None))
            prevent_registration = bool(request.form.get('prevent_registration', None))
            prevent_name_change = bool(request.form.get('prevent_name_change', None))
            view_after_ctf = bool(request.form.get('view_after_ctf', None))
        except (ValueError, TypeError):
            view_challenges_unregistered = None
            prevent_registration = None
            prevent_name_change = None
            view_after_ctf = None
        finally:
            view_challenges_unregistered = set_config('view_challenges_unregistered', view_challenges_unregistered)
            prevent_registration = set_config('prevent_registration', prevent_registration)
            prevent_name_change = set_config('prevent_name_change', prevent_name_change)
            view_after_ctf = set_config('view_after_ctf', view_after_ctf)

        ctf_name = set_config("ctf_name", request.form.get('ctf_name', None))
        mg_api_key = set_config("mg_api_key", request.form.get('mg_api_key', None))
        max_tries = set_config("max_tries", request.form.get('max_tries', None))


        db_start = Config.query.filter_by(key='start').first()
        db_start.value = start

        db_end = Config.query.filter_by(key='end').first()
        db_end.value = end

        db.session.add(db_start)
        db.session.add(db_end)

        db.session.commit()
        return redirect('/admin/config')

    ctf_name = get_config('ctf_name')
    if not ctf_name:
        set_config('ctf_name', None)

    mg_api_key = get_config('mg_api_key')
    if not mg_api_key:
        set_config('mg_api_key', None)

    max_tries = get_config('max_tries')
    if not max_tries:
        set_config('max_tries', 0)
        max_tries = 0

    view_after_ctf = get_config('view_after_ctf') == '1'
    if not view_after_ctf:
        set_config('view_after_ctf', 0)
        view_after_ctf = 0

    start = get_config('start')
    if not start:
        set_config('start', None)

    end = get_config('end')
    if not end:
        set_config('end', None)

    view_challenges_unregistered = get_config('view_challenges_unregistered') == '1'
    if not view_challenges_unregistered:
        set_config('view_challenges_unregistered', None)

    prevent_registration = get_config('prevent_registration') == '1'
    if not prevent_registration:
        set_config('prevent_registration', None)

    prevent_name_change = get_config('prevent_name_change') == '1'
    if not prevent_name_change:
        set_config('prevent_name_change', None)

    db.session.commit()
    db.session.close()

    return render_template('admin/config.html', ctf_name=ctf_name, start=start, end=end,
                           max_tries=max_tries,
                           view_challenges_unregistered=view_challenges_unregistered,
                           prevent_registration=prevent_registration, mg_api_key=mg_api_key,
                           prevent_name_change=prevent_name_change,
                           view_after_ctf=view_after_ctf)
Example #29
0
def admin_config():
    if request.method == "POST":
        start = None
        end = None
        if request.form.get('start'):
            start = int(request.form['start'])
        if request.form.get('end'):
            end = int(request.form['end'])
            if end < unix_time(datetime.datetime.now()):
                end = None

        try:
            view_challenges_unregistered = bool(request.form.get('view_challenges_unregistered', None))
            view_scoreboard_if_authed = bool(request.form.get('view_scoreboard_if_authed', None))
            prevent_registration = bool(request.form.get('prevent_registration', None))
            prevent_name_change = bool(request.form.get('prevent_name_change', None))
            view_after_ctf = bool(request.form.get('view_after_ctf', None))
            verify_emails = bool(request.form.get('verify_emails', None))
            mail_tls = bool(request.form.get('mail_tls', None))
            mail_ssl = bool(request.form.get('mail_ssl', None))
        except (ValueError, TypeError):
            view_challenges_unregistered = None
            view_scoreboard_if_authed = None
            prevent_registration = None
            prevent_name_change = None
            view_after_ctf = None
            verify_emails = None
            mail_tls = None
            mail_ssl = None
        finally:
            view_challenges_unregistered = set_config('view_challenges_unregistered', view_challenges_unregistered)
            view_scoreboard_if_authed = set_config('view_scoreboard_if_authed', view_scoreboard_if_authed)
            prevent_registration = set_config('prevent_registration', prevent_registration)
            prevent_name_change = set_config('prevent_name_change', prevent_name_change)
            view_after_ctf = set_config('view_after_ctf', view_after_ctf)
            verify_emails = set_config('verify_emails', verify_emails)
            mail_tls = set_config('mail_tls', mail_tls)
            mail_ssl = set_config('mail_ssl', mail_ssl)

        mail_server = set_config("mail_server", request.form.get('mail_server', None))
        mail_port = set_config("mail_port", request.form.get('mail_port', None))

        mail_username = set_config("mail_username", request.form.get('mail_username', None))
        mail_password = set_config("mail_password", request.form.get('mail_password', None))

        ctf_name = set_config("ctf_name", request.form.get('ctf_name', None))

        mg_base_url = set_config("mg_base_url", request.form.get('mg_base_url', None))
        mg_api_key = set_config("mg_api_key", request.form.get('mg_api_key', None))

        max_tries = set_config("max_tries", request.form.get('max_tries', None))

        db_start = Config.query.filter_by(key='start').first()
        db_start.value = start

        db_end = Config.query.filter_by(key='end').first()
        db_end.value = end

        db.session.add(db_start)
        db.session.add(db_end)

        db.session.commit()
        return redirect(url_for('admin.admin_config'))

    ctf_name = get_config('ctf_name')
    max_tries = get_config('max_tries')

    mail_server = get_config('mail_server')
    mail_port = get_config('mail_port')
    mail_username = get_config('mail_username')
    mail_password = get_config('mail_password')

    mg_api_key = get_config('mg_api_key')
    mg_base_url = get_config('mg_base_url')
    if not max_tries:
        set_config('max_tries', 0)
        max_tries = 0

    view_after_ctf = get_config('view_after_ctf')
    start = get_config('start')
    end = get_config('end')

    mail_tls = get_config('mail_tls')
    mail_ssl = get_config('mail_ssl')

    view_challenges_unregistered = get_config('view_challenges_unregistered')
    view_scoreboard_if_authed = get_config('view_scoreboard_if_authed')
    prevent_registration = get_config('prevent_registration')
    prevent_name_change = get_config('prevent_name_change')
    verify_emails = get_config('verify_emails')

    db.session.commit()
    db.session.close()

    months = [
        'January', 'February', 'March', 'April',
        'May', 'June', 'July', 'August',
        'September', 'October', 'November', 'December'
    ]

    curr_year = datetime.date.today().year
    start_days = 0
    end_days = 0

    if start:
        start = datetime.datetime.fromtimestamp(float(start))
        start_days = calendar.monthrange(start.year, start.month)[1]
    if end:
        end = datetime.datetime.fromtimestamp(float(end))
        end_days = calendar.monthrange(end.year, end.month)[1]

    return render_template('admin/config.html',
                           ctf_name=ctf_name,
                           start=start,
                           end=end,
                           max_tries=max_tries,
                           mail_server=mail_server,
                           mail_port=mail_port,
                           mail_username=mail_username,
                           mail_password=mail_password,
                           mail_tls=mail_tls,
                           mail_ssl=mail_ssl,
                           view_challenges_unregistered=view_challenges_unregistered,
                           view_scoreboard_if_authed=view_scoreboard_if_authed,
                           prevent_registration=prevent_registration,
                           mg_base_url=mg_base_url,
                           mg_api_key=mg_api_key,
                           prevent_name_change=prevent_name_change,
                           verify_emails=verify_emails,
                           view_after_ctf=view_after_ctf,
                           months=months,
                           curr_year=curr_year,
                           start_days=start_days,
                           end_days=end_days)
Example #30
0
def profile():
    if utils.authed():
        if request.method == "POST":
            errors = []

            name = request.form.get('name').strip()
            email = request.form.get('email').strip()
            website = request.form.get('website').strip()
            affiliation = request.form.get('affiliation').strip()
            country = request.form.get('country').strip()

            user = Teams.query.filter_by(id=session['id']).first()

            if not utils.get_config('prevent_name_change'):
                names = Teams.query.filter_by(name=name).first()
                name_len = len(request.form['name']) == 0

            emails = Teams.query.filter_by(email=email).first()
            valid_email = utils.check_email_format(email)

            if utils.check_email_format(name) is True:
                errors.append('Team name cannot be an email address')

            if ('password' in request.form.keys() and not len(request.form['password']) == 0) and \
                    (not bcrypt_sha256.verify(request.form.get('confirm').strip(), user.password)):
                errors.append("Your old password doesn't match what we have.")
            if not valid_email:
                errors.append("That email doesn't look right")
            if not utils.get_config(
                    'prevent_name_change'
            ) and names and name != session['username']:
                errors.append('That team name is already taken')
            if emails and emails.id != session['id']:
                errors.append('That email has already been used')
            if not utils.get_config('prevent_name_change') and name_len:
                errors.append('Pick a longer team name')
            if website.strip() and not utils.validate_url(website):
                errors.append("That doesn't look like a valid URL")

            if len(errors) > 0:
                return render_template('profile.html',
                                       name=name,
                                       email=email,
                                       website=website,
                                       affiliation=affiliation,
                                       country=country,
                                       errors=errors)
            else:
                team = Teams.query.filter_by(id=session['id']).first()
                if team.name != name:
                    if not utils.get_config('prevent_name_change'):
                        team.name = name
                        session['username'] = team.name
                if team.email != email.lower():
                    team.email = email.lower()
                    if utils.get_config('verify_emails'):
                        team.verified = False

                if 'password' in request.form.keys() and not len(
                        request.form['password']) == 0:
                    team.password = bcrypt_sha256.encrypt(
                        request.form.get('password'))
                team.website = website
                team.affiliation = affiliation
                team.country = country
                db.session.commit()
                db.session.close()
                return redirect(url_for('views.profile'))
        else:
            user = Teams.query.filter_by(id=session['id']).first()
            name = user.name
            email = user.email
            website = user.website
            affiliation = user.affiliation
            country = user.country
            prevent_name_change = utils.get_config('prevent_name_change')
            confirm_email = utils.get_config(
                'verify_emails') and not user.verified
            return render_template('profile.html',
                                   name=name,
                                   email=email,
                                   website=website,
                                   affiliation=affiliation,
                                   country=country,
                                   prevent_name_change=prevent_name_change,
                                   confirm_email=confirm_email)
    else:
        return redirect(url_for('auth.login'))
Example #31
0
def chal(chalid):
    if not ctftime():
        return redirect(url_for('challenges.challenges_view'))
    if authed():
        fails = WrongKeys.query.filter_by(teamid=session['id'], chalid=chalid).count()
        logger = logging.getLogger('keys')
        data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), request.form['key'].encode('utf-8'), get_kpm(session['id']))
        print("[{0}] {1} submitted {2} with kpm {3}".format(*data))

        # Hit max attempts
        if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
            return "4" #too many tries on this challenge

        # Anti-bruteforce / submitting keys too quickly
        if get_kpm(session['id']) > 10:
            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.warn("[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(*data))
            return "3" # Submitting too fast

        solves = Solves.query.filter_by(teamid=session['id'], chalid=chalid).first()

        # Challange not solved yet
        if not solves:
            chal = Challenges.query.filter_by(id=chalid).first()
            key = str(request.form['key'].strip().lower())
            keys = json.loads(chal.flags)
            for x in keys:
                if x['type'] == 0: #static key
                    print(x['flag'], key.strip().lower())
                    if x['flag'] and x['flag'].strip().lower() == key.strip().lower():
                        solve = Solves(chalid=chalid, teamid=session['id'], ip=get_ip(), flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
                        return "1" # key was correct
                elif x['type'] == 1: #regex
                    res = re.match(str(x['flag']), key, re.IGNORECASE)
                    if res and res.group() == key:
                        solve = Solves(chalid=chalid, teamid=session['id'], ip=get_ip(), flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
                        return "1" # key was correct

            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
            return '0' # key was wrong

        # Challenge already solved
        else:
            logger.info("{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format(*data))
            return "2" # challenge was already solved
    else:
        return "-1"
Example #32
0
def custom_css():
    return Response(get_config("css"), mimetype='text/css')
def mlc():
    return get_config("oauth_client_id") and get_config("oauth_client_secret")
Example #34
0
    def check_env(challenge_id):
        from .schedule import scheduler
        with scheduler.app.app_context():
            try:
                mode = utils.get_config("user_mode")
                platform_name = utils.get_config("ctf_name")
                basePath = os.path.abspath(os.path.dirname(__file__))
                platformDir = os.path.join(basePath, platform_name)
                challenge = ADAChallenge.query.filter_by(
                    id=challenge_id).first_or_404()
                dirname = challenge.dirname.split("/")[1]
                containers = GlowwormContainers.query.filter_by(
                    challenge_id=challenge_id).all()
                for index in containers:
                    if mode == "users":
                        victim = Users.query.filter_by(
                            id=index.user_id).first()
                        victim_name = victim.name
                        victim_id = victim.id
                        team_id = victim.team_id if victim.team_id else None
                    else:
                        victim = None
                        team = Teams.query.filter_by(id=index.user_id).first()
                        team_id = team.id
                        victim_id = team_id
                        victim_name = team.name

                    check_file = os.path.join(basePath, challenge.dirname,
                                              "conf", "check.py")
                    # Todo: excute check file in containers
                    command = "python3 '%s' %s %s" % (check_file, index.ip,
                                                      index.service_port)
                    print(command)
                    # 容器Check
                    rq = os.popen(command).read()
                    r = "".join(re.findall(r'team..', rq))
                    if r == "":
                        msg = index.docker_id + " seems ok."
                    else:
                        msg = index.docker_id + " seems down."
                        check_log = GlowwormCheckLog(
                            user_id=victim_id,
                            team_id=team_id,
                            victim_user_id=victim_id,
                            victim_team_id=team_id,
                            challenge_id=challenge.id,
                            ip="127.0.0.1",
                            provided=msg,
                        )
                        check = GlowwormAttacks(attack_id=None,
                                                attack_name=None,
                                                victim_id=victim_id,
                                                victim_name=victim_name,
                                                docker_id=index.docker_id,
                                                envname=index.docker_id.split(
                                                    "_", 1)[1],
                                                flag="",
                                                round=get_round())
                        db.session.add(check)
                        db.session.add(check_log)
                        print(check)
                        print(check_log)
                    print(msg)
                db.session.commit()
                db.session.close()
                return True
            except Exception as e:
                print(e)
                return False
Example #35
0
def register():
    if not can_register():
        return redirect(url_for('auth.login', _external=True))
    if request.method == 'POST':
        errors = []
        name = request.form['name']
        email = request.form['email']
        password = request.form['password']

        name_len = len(name) == 0
        names = Teams.query.add_columns('name',
                                        'id').filter_by(name=name).first()
        emails = Teams.query.add_columns('email',
                                         'id').filter_by(email=email).first()
        pass_short = len(password) == 0
        pass_long = len(password) > 128
        valid_email = re.match("[^@]+@[^@]+\.[^@]+", request.form['email'])

        if not valid_email:
            errors.append("That email doesn't look right")
        if names:
            errors.append('That team name is already taken')
        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 team name')

        if len(errors) > 0:
            return render_template('register.html',
                                   errors=errors,
                                   name=request.form['name'],
                                   email=request.form['email'],
                                   password=request.form['password'])
        else:
            with app.app_context():
                team = Teams(name, email.lower(), password)
                db.session.add(team)
                db.session.commit()
                db.session.flush()

                session['username'] = team.name
                session['id'] = team.id
                session['admin'] = team.admin
                session['nonce'] = sha512(os.urandom(10))

                if can_send_mail() and get_config('verify_emails'):
                    verify_email(team.email)
                else:
                    if can_send_mail():
                        sendmail(
                            request.form['email'],
                            "You've successfully registered for {}".format(
                                get_config('ctf_name')))

        db.session.close()

        logger = logging.getLogger('regs')
        logger.warn("[{0}] {1} registered with {2}".format(
            time.strftime("%m/%d/%Y %X"), request.form['name'].encode('utf-8'),
            request.form['email'].encode('utf-8')))
        return redirect(url_for('challenges.challenges_view', _external=True))
    else:
        return render_template('register.html')
Example #36
0
    def run_env(counts, challenge_id):
        try:
            configs = DBUtils.get_all_configs()
            interval = configs.get("per_round") if configs.get(
                "per_round") else "300"
            cpu_limit = configs.get("cpu_limit") if configs.get(
                "cpu_limit") else "0.5"
            memory_limit = configs.get("memory_limit") if configs.get(
                "memory_limit") else "512M"
            rootpwd = configs.get("containers_key") if configs.get(
                "containers_key") else "root"
            mode = utils.get_config("user_mode")
            platform_name = utils.get_config("ctf_name")
            basePath = os.path.abspath(os.path.dirname(__file__))
            platformDir = os.path.join(basePath, platform_name)
            challenge = ADAChallenge.query.filter_by(
                id=challenge_id).first_or_404()
            for index in counts:
                if mode == "users" and index.type == "admin":
                    pass
                else:
                    dirname = challenge.dirname.split("/")[1]
                    envPath = os.path.join(basePath, challenge.dirname)
                    teamDir = os.path.join(basePath, platform_name,
                                           'team' + str(index.id))
                    teamEnvDir = os.path.join(teamDir, dirname)
                    name = "Team{}_{}".format(str(index.id), dirname)
                    # 目录复制
                    DockerUtils.copy_env(envPath, teamDir)
                    # 容器动态信息初始化
                    # save random key
                    key = str(
                        uuid.uuid3(uuid.UUID(str(uuid.uuid1())),
                                   platform_name + str(time.time())))
                    print(key)
                    instance_pwd = DockerUtils.gen_env(
                        language=challenge.env_language,
                        rootpasswd=rootpwd,
                        teamPath=teamEnvDir,
                        key=key,
                        teamid=name,
                        interval=interval)
                    # choose alive random port
                    if configs.get("random_port") == "1":
                        fixedPorts = DBUtils.get_alive_ports()
                        insert_service_port = fixedPorts[0]
                        insert_ssh_port = fixedPorts[1]
                    else:
                        fixedPorts = 100 * int(challenge.id)
                        insert_service_port = int(
                            str(fixedPorts + index.id) + "80")
                        insert_ssh_port = int(
                            str(fixedPorts + index.id) + "22")
                    env_port = challenge.env_port if challenge.env_port != "" else "80"
                    confPath = os.path.join(teamEnvDir, "conf")

                    instance = GlowwormContainers(
                        user_id=index.id,
                        challenge_id=challenge_id,
                        docker_id=name,
                        ip=configs.get("direct_address"),
                        service_port=insert_service_port,
                        ssh_port=insert_ssh_port,
                        ssh_key=instance_pwd,
                        key=key)
                    db.session.add(instance)
                    db.session.commit()
                    command = """#!/bin/sh
    docker run -tid --restart=on-failure:10 --privileged --name %s --cpus=%s -m %s -v "%s":"%s" -p %s:%s -p %s:%s --network h1ve_frp_containers %s "/conf/service.sh"
    """ % (name, cpu_limit, memory_limit, confPath, "/conf",
                    insert_service_port, env_port, insert_ssh_port, "22",
                    challenge.image_name)
                    print(command)
                    with open(os.path.join(confPath, "docker.sh"), 'w') as f:
                        f.write(command)
                    # 启动容器
                    command = 'cd "%s" && chmod +x ./docker.sh service.sh && ./docker.sh' % confPath
                    print(command)
                    try:
                        os.system(command)
                        msg = name + " up."
                        log(
                            "glowworm",
                            "[{date}] {name} {msg}",
                            msg=msg,
                        )

                    except Exception as e:
                        # print(e)
                        msg = name + " up error." + str(e)
                        log(
                            "glowworm",
                            "[{date}] {name} {msg}",
                            msg=msg,
                        )
                        return str(e)
            challenge.env_status = True
            db.session.commit()
            return True
        except Exception as e:
            return str(e)
Example #37
0
def oauth_redirect():
    oauth_code = request.args.get("code")
    state = request.args.get("state")
    if session["nonce"] != state:
        log("logins", "[{date}] {ip} - OAuth State validation mismatch")
        error_for(endpoint="auth.login",
                  message="OAuth State validation mismatch.")
        return redirect(url_for("auth.login"))

    if oauth_code:
        url = (get_app_config("OAUTH_TOKEN_ENDPOINT")
               or get_config("oauth_token_endpoint")
               or "https://auth.majorleaguecyber.org/oauth/token")

        client_id = get_app_config("OAUTH_CLIENT_ID") or get_config(
            "oauth_client_id")
        client_secret = get_app_config("OAUTH_CLIENT_SECRET") or get_config(
            "oauth_client_secret")
        headers = {"content-type": "application/x-www-form-urlencoded"}
        data = {
            "code": oauth_code,
            "client_id": client_id,
            "client_secret": client_secret,
            "grant_type": "authorization_code",
        }
        token_request = requests.post(url, data=data, headers=headers)

        if token_request.status_code == requests.codes.ok:
            token = token_request.json()["access_token"]
            user_url = (get_app_config("OAUTH_API_ENDPOINT")
                        or get_config("oauth_api_endpoint")
                        or "https://api.majorleaguecyber.org/user")

            headers = {
                "Authorization": "Bearer " + str(token),
                "Content-type": "application/json",
            }
            api_data = requests.get(url=user_url, headers=headers).json()

            user_id = api_data["id"]
            user_name = api_data["name"]
            user_email = api_data["email"]

            user = Users.query.filter_by(email=user_email).first()
            if user is None:
                # Check if we are allowing registration before creating users
                if registration_visible() or mlc_registration():
                    user = Users(
                        name=user_name,
                        email=user_email,
                        oauth_id=user_id,
                        verified=True,
                    )
                    db.session.add(user)
                    db.session.commit()
                else:
                    log("logins",
                        "[{date}] {ip} - Public registration via MLC blocked")
                    error_for(
                        endpoint="auth.login",
                        message=
                        "Public registration is disabled. Please try again later.",
                    )
                    return redirect(url_for("auth.login"))

            if get_config("user_mode") == TEAMS_MODE:
                team_id = api_data["team"]["id"]
                team_name = api_data["team"]["name"]

                team = Teams.query.filter_by(oauth_id=team_id).first()
                if team is None:
                    team = Teams(name=team_name,
                                 oauth_id=team_id,
                                 captain_id=user.id)
                    db.session.add(team)
                    db.session.commit()
                    clear_team_session(team_id=team.id)

                team_size_limit = get_config("team_size", default=0)
                if team_size_limit and len(team.members) >= team_size_limit:
                    plural = "" if team_size_limit == 1 else "s"
                    size_error = "Teams are limited to {limit} member{plural}.".format(
                        limit=team_size_limit, plural=plural)
                    error_for(endpoint="auth.login", message=size_error)
                    return redirect(url_for("auth.login"))

                team.members.append(user)
                db.session.commit()

            if user.oauth_id is None:
                user.oauth_id = user_id
                user.verified = True
                db.session.commit()
                clear_user_session(user_id=user.id)

            login_user(user)

            return redirect(url_for("challenges.listing"))
        else:
            log("logins", "[{date}] {ip} - OAuth token retrieval failure")
            error_for(endpoint="auth.login",
                      message="OAuth token retrieval failure.")
            return redirect(url_for("auth.login"))
    else:
        log("logins", "[{date}] {ip} - Received redirect without OAuth code")
        error_for(endpoint="auth.login",
                  message="Received redirect without OAuth code.")
        return redirect(url_for("auth.login"))
Example #38
0
def register():
    errors = get_errors()
    if current_user.authed():
        return redirect(url_for("challenges.listing"))

    if request.method == "POST":
        name = request.form.get("name", "").strip()
        email_address = request.form.get("email", "").strip().lower()
        password = request.form.get("password", "").strip()

        website = request.form.get("website")
        affiliation = request.form.get("affiliation")
        country = request.form.get("country")

        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_address).first())
        pass_short = len(password) == 0
        pass_long = len(password) > 128
        valid_email = validators.validate_email(email_address)
        team_name_email_check = validators.validate_email(name)

        # Process additional user fields
        fields = {}
        for field in UserFields.query.all():
            fields[field.id] = field

        entries = {}
        for field_id, field in fields.items():
            value = request.form.get(f"fields[{field_id}]", "").strip()
            if field.required is True and (value is None or value == ""):
                errors.append("Please provide all required fields")
                break

            # Handle special casing of existing profile fields
            if field.name.lower() == "affiliation":
                affiliation = value
                break
            elif field.name.lower() == "website":
                website = value
                break

            if field.field_type == "boolean":
                entries[field_id] = bool(value)
            else:
                entries[field_id] = value

        if country:
            try:
                validators.validate_country_code(country)
                valid_country = True
            except ValidationError:
                valid_country = False
        else:
            valid_country = True

        if website:
            valid_website = validators.validate_url(website)
        else:
            valid_website = True

        if affiliation:
            valid_affiliation = len(affiliation) < 128
        else:
            valid_affiliation = True

        if not valid_email:
            errors.append("Please enter a valid email address")
        if email.check_email_is_whitelisted(email_address) is False:
            errors.append(
                "Only email addresses under {domains} may register".format(
                    domains=get_config("domain_whitelist")))
        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 valid_website is False:
            errors.append(
                "Websites must be a proper URL starting with http or https")
        if valid_country is False:
            errors.append("Invalid country")
        if valid_affiliation is False:
            errors.append("Please provide a shorter affiliation")

        if len(errors) > 0:
            return render_template(
                "register.html",
                errors=errors,
                name=request.form["name"],
                email=request.form["email"],
                password=request.form["password"],
            )
        else:
            with app.app_context():
                user = Users(name=name, email=email_address, password=password)

                if website:
                    user.website = website
                if affiliation:
                    user.affiliation = affiliation
                if country:
                    user.country = country

                db.session.add(user)
                db.session.commit()
                db.session.flush()

                for field_id, value in entries.items():
                    entry = UserFieldEntries(field_id=field_id,
                                             value=value,
                                             user_id=user.id)
                    db.session.add(entry)
                db.session.commit()

                login_user(user)

                if request.args.get("next") and validators.is_safe_url(
                        request.args.get("next")):
                    return redirect(request.args.get("next"))

                if config.can_send_mail() and get_config(
                        "verify_emails"
                ):  # Confirming users is enabled and we can send email.
                    log(
                        "registrations",
                        format=
                        "[{date}] {ip} - {name} registered (UNCONFIRMED) with {email}",
                        name=user.name,
                        email=user.email,
                    )
                    email.verify_email_address(user.email)
                    db.session.close()
                    return redirect(url_for("auth.confirm"))
                else:  # Don't care about confirming users
                    if (
                            config.can_send_mail()
                    ):  # We want to notify the user that they have registered.
                        email.successful_registration_notification(user.email)

        log(
            "registrations",
            format="[{date}] {ip} - {name} registered with {email}",
            name=user.name,
            email=user.email,
        )
        db.session.close()

        if is_teams_mode():
            return redirect(url_for("teams.private"))

        return redirect(url_for("challenges.listing"))
    else:
        return render_template("register.html", errors=errors)
Example #39
0
def test_admin_post_config_values():
    """Test that admins can POST configuration values"""
    app = create_ctfd()
    with app.app_context():
        client = login_as_user(app, name="admin", password="******")

        with client.session_transaction() as sess:
            data = {
                'nonce': sess.get('nonce'),
                'ctf_name': 'CTFd',
                'ctf_logo': '',
                'ctf_theme': 'core',
                'workshop_mode': 'on',
                'paused': 'on',
                'hide_scores': 'on',
                'css': 'None',
                'verify_emails': 'on',
                'view_challenges_unregistered': 'on',
                'view_scoreboard_if_authed': 'on',
                'prevent_registration': 'on',
                'prevent_name_change': 'on',
                'mailfrom_addr': '*****@*****.**',
                'mail_server': 'mail.failmail.com',
                'mail_port': '587',
                'mail_useauth': 'on',
                'mail_u': 'username',
                'mail_p': 'password',
                'mail_tls': 'on',
                'mg_base_url': '',
                'mg_api_key': '',
                'start-month': '',
                'start-day': '',
                'start-year': '',
                'start-hour': '',
                'start-minute': '',
                'start': '',
                'end-month': '',
                'end-day': '',
                'end-year': '',
                'end-hour': '',
                'end-minute': '',
                'end': '',
                'freeze-month': '',
                'freeze-day': '',
                'freeze-year': '',
                'freeze-hour': '',
                'freeze-minute': '',
                'freeze': '',
                'backup': ''
            }
            r = client.post('/admin/config', data=data)

        result = {
            'ctf_name': 'CTFd',
            'ctf_logo': None,
            'ctf_theme': 'core',
            'workshop_mode': True,
            'paused': True,
            'hide_scores': True,
            'css': 'None',
            'verify_emails': True,
            'view_challenges_unregistered': True,
            'view_scoreboard_if_authed': True,
            'prevent_registration': True,
            'prevent_name_change': True,
            'mailfrom_addr': '*****@*****.**',
            'mail_server': 'mail.failmail.com',
            'mail_port': 587,
            'mail_useauth': True,
            'mail_username': '******',
            'mail_password': '******',
            'mail_tls': True,
            'mg_base_url': None,
            'mg_api_key': None,
            'start-month': None,
            'start-day': None,
            'start-year': None,
            'start-hour': None,
            'start-minute': None,
            'start': None,
            'end-month': None,
            'end-day': None,
            'end-year': None,
            'end-hour': None,
            'end-minute': None,
            'end': None,
            'freeze-month': None,
            'freeze-day': None,
            'freeze-year': None,
            'freeze-hour': None,
            'freeze-minute': None,
            'freeze': None,
            'backup': None
        }

        for key in result:
            if result[key]:
                assert get_config(key) == result[key]
            else:
                assert get_config(key) is None

    destroy_ctfd(app)
Example #40
0
def create_app(config="CTFd.config.Config"):
    app = CTFdFlask(__name__)
    with app.app_context():
        app.config.from_object(config)

        theme_loader = ThemeLoader(
            os.path.join(app.root_path, "themes"), followlinks=True
        )
        app.jinja_loader = theme_loader

        from CTFd.models import (  # noqa: F401
            db,
            Teams,
            Solves,
            Challenges,
            Fails,
            Flags,
            Tags,
            Files,
            Tracking,
        )

        url = create_database()

        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
        # This is mostly so we can force MySQL's charset
        app.config["SQLALCHEMY_DATABASE_URI"] = str(url)

        # Register database
        db.init_app(app)

        # Register Flask-Migrate
        migrations.init_app(app, db)

        # Alembic sqlite support is lacking so we should just create_all anyway
        if url.drivername.startswith("sqlite"):
            db.create_all()
            stamp_latest_revision()
        else:
            # This creates tables instead of db.create_all()
            # Allows migrations to happen properly
            upgrade()

        from CTFd.models import ma

        ma.init_app(app)

        app.db = db
        app.VERSION = __version__

        from CTFd.cache import cache

        cache.init_app(app)
        app.cache = cache

        reverse_proxy = app.config.get("REVERSE_PROXY")
        if reverse_proxy:
            if "," in reverse_proxy:
                proxyfix_args = [int(i) for i in reverse_proxy.split(",")]
                app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)
            else:
                app.wsgi_app = ProxyFix(
                    app.wsgi_app,
                    num_proxies=None,
                    x_for=1,
                    x_proto=1,
                    x_host=1,
                    x_port=1,
                    x_prefix=1,
                )

        version = utils.get_config("ctf_version")

        # Upgrading from an older version of CTFd
        if version and (StrictVersion(version) < StrictVersion(__version__)):
            if confirm_upgrade():
                run_upgrade()
            else:
                exit()

        if not version:
            utils.set_config("ctf_version", __version__)

        if not utils.get_config("ctf_theme"):
            utils.set_config("ctf_theme", "core")

        update_check(force=True)

        init_request_processors(app)
        init_template_filters(app)
        init_template_globals(app)

        # Importing here allows tests to use sensible names (e.g. api instead of api_bp)
        from CTFd.views import views
        from CTFd.teams import teams
        from CTFd.users import users
        from CTFd.challenges import challenges
        from CTFd.scoreboard import scoreboard
        from CTFd.auth import auth
        from CTFd.admin import admin
        from CTFd.api import api
        from CTFd.events import events
        from CTFd.errors import page_not_found, forbidden, general_error, gateway_error

        app.register_blueprint(views)
        app.register_blueprint(teams)
        app.register_blueprint(users)
        app.register_blueprint(challenges)
        app.register_blueprint(scoreboard)
        app.register_blueprint(auth)
        app.register_blueprint(api)
        app.register_blueprint(events)

        app.register_blueprint(admin)

        app.register_error_handler(404, page_not_found)
        app.register_error_handler(403, forbidden)
        app.register_error_handler(500, general_error)
        app.register_error_handler(502, gateway_error)

        init_logs(app)
        init_events(app)
        init_plugins(app)

        return app
Example #41
0
def user_mode():
    return get_config("user_mode")
Example #42
0
def custom_css():
    return Response(utils.get_config('css'), mimetype='text/css')
def mlc_registration():
    v = get_config("registration_visibility")
    return v == "mlc"
Example #44
0
File: views.py Project: ajvpot/CTFd
def profile():
    if authed():
        if request.method == "POST":
            errors = []

            name = request.form.get('name')
            email = request.form.get('email')
            website = request.form.get('website')
            affiliation = request.form.get('affiliation')
            country = request.form.get('country')

            user = Teams.query.filter_by(id=session['id']).first()

            if not get_config('prevent_name_change'):
                names = Teams.query.filter_by(name=name).first()
                name_len = len(request.form['name']) == 0

            emails = Teams.query.filter_by(email=email).first()
            valid_email = re.match("[^@]+@[^@]+\.[^@]+", email)

            if ('password' in request.form.keys() and not len(request.form['password']) == 0) and \
                    (not bcrypt_sha256.verify(request.form.get('confirm').strip(), user.password)):
                errors.append("Your old password doesn't match what we have.")
            if not valid_email:
                errors.append("That email doesn't look right")
            if not get_config('prevent_name_change') and names and name!=session['username']:
                errors.append('That team name is already taken')
            if emails and emails.id != session['id']:
                errors.append('That email has already been used')
            if not get_config('prevent_name_change') and name_len:
                errors.append('Pick a longer team name')
            if website.strip() and not validate_url(website):
                errors.append("That doesn't look like a valid URL")

            if len(errors) > 0:
                return render_template('profile.html', name=name, email=email, website=website,
                                       affiliation=affiliation, country=country, errors=errors)
            else:
                team = Teams.query.filter_by(id=session['id']).first()
                if not get_config('prevent_name_change'):
                    team.name = name
                team.email = email
                session['username'] = team.name

                if 'password' in request.form.keys() and not len(request.form['password']) == 0:
                    team.password = bcrypt_sha256.encrypt(request.form.get('password'))
                team.website = website
                team.affiliation = affiliation
                team.country = country
                db.session.commit()
                db.session.close()
                return redirect('/profile')
        else:
            user = Teams.query.filter_by(id=session['id']).first()
            name = user.name
            email = user.email
            website = user.website
            affiliation = user.affiliation
            country = user.country
            prevent_name_change = get_config('prevent_name_change')
            return render_template('profile.html', name=name, email=email, website=website, affiliation=affiliation,
                                   country=country, prevent_name_change=prevent_name_change)
    else:
        return redirect('/login')
Example #45
0
def create_app(config="CTFd.config.Config"):
    app = CTFdFlask(__name__)
    with app.app_context():
        app.config.from_object(config)

        app.theme_loader = ThemeLoader(os.path.join(app.root_path, "themes"),
                                       followlinks=True)
        # Weird nested solution for accessing plugin templates
        app.plugin_loader = jinja2.PrefixLoader({
            "plugins":
            jinja2.FileSystemLoader(searchpath=os.path.join(
                app.root_path, "plugins"),
                                    followlinks=True)
        })
        # Load from themes first but fallback to loading from the plugin folder
        app.jinja_loader = jinja2.ChoiceLoader(
            [app.theme_loader, app.plugin_loader])

        from CTFd.models import (  # noqa: F401
            db, Teams, Solves, Challenges, Fails, Flags, Tags, Files, Tracking,
        )

        url = create_database()

        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
        # This is mostly so we can force MySQL's charset
        app.config["SQLALCHEMY_DATABASE_URI"] = str(url)

        # Register database
        db.init_app(app)

        # Register Flask-Migrate
        migrations.init_app(app, db)

        # Alembic sqlite support is lacking so we should just create_all anyway
        if url.drivername.startswith("sqlite"):
            # Enable foreign keys for SQLite. This must be before the
            # db.create_all call because tests use the in-memory SQLite
            # database (each connection, including db creation, is a new db).
            # https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#foreign-key-support
            from sqlalchemy.engine import Engine
            from sqlalchemy import event

            @event.listens_for(Engine, "connect")
            def set_sqlite_pragma(dbapi_connection, connection_record):
                cursor = dbapi_connection.cursor()
                cursor.execute("PRAGMA foreign_keys=ON")
                cursor.close()

            db.create_all()
            stamp_latest_revision()
        else:
            # This creates tables instead of db.create_all()
            # Allows migrations to happen properly
            upgrade()

        from CTFd.models import ma

        ma.init_app(app)

        app.db = db
        app.VERSION = __version__
        app.CHANNEL = __channel__

        from CTFd.cache import cache

        cache.init_app(app)
        app.cache = cache

        reverse_proxy = app.config.get("REVERSE_PROXY")
        if reverse_proxy:
            if type(reverse_proxy) is str and "," in reverse_proxy:
                proxyfix_args = [int(i) for i in reverse_proxy.split(",")]
                app.wsgi_app = ProxyFix(app.wsgi_app, *proxyfix_args)
            else:
                app.wsgi_app = ProxyFix(app.wsgi_app,
                                        x_for=1,
                                        x_proto=1,
                                        x_host=1,
                                        x_port=1,
                                        x_prefix=1)

        version = utils.get_config("ctf_version")

        # Upgrading from an older version of CTFd
        if version and (StrictVersion(version) < StrictVersion(__version__)):
            if confirm_upgrade():
                run_upgrade()
            else:
                exit()

        if not version:
            utils.set_config("ctf_version", __version__)

        if not utils.get_config("ctf_theme"):
            utils.set_config("ctf_theme", "core")

        update_check(force=True)

        init_request_processors(app)
        init_template_filters(app)
        init_template_globals(app)

        # Importing here allows tests to use sensible names (e.g. api instead of api_bp)
        from CTFd.views import views
        from CTFd.teams import teams
        from CTFd.users import users
        from CTFd.challenges import challenges
        from CTFd.scoreboard import scoreboard
        from CTFd.auth import auth
        from CTFd.admin import admin
        from CTFd.api import api
        from CTFd.events import events
        from CTFd.errors import page_not_found, forbidden, general_error, gateway_error

        app.register_blueprint(views)
        app.register_blueprint(teams)
        app.register_blueprint(users)
        app.register_blueprint(challenges)
        app.register_blueprint(scoreboard)
        app.register_blueprint(auth)
        app.register_blueprint(api)
        app.register_blueprint(events)

        app.register_blueprint(admin)

        app.register_error_handler(404, page_not_found)
        app.register_error_handler(403, forbidden)
        app.register_error_handler(500, general_error)
        app.register_error_handler(502, gateway_error)

        init_logs(app)
        init_events(app)
        init_plugins(app)

        return app
Example #46
0
    def get(self, config_key):

        return {'success': True, 'data': get_config(config_key)}
Example #47
0
File: views.py Project: ajvpot/CTFd
def custom_css():
    return Response(get_config("css"), mimetype='text/css')
Example #48
0
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("请输入有效的电子邮件地址")
            if names:
                errors.append("该用户名以及被使用")
            if team_name_email_check is True:
                errors.append("您的用户名不能是电子邮件地址")
            if emails:
                errors.append("电子邮件地址已被使用")
            if pass_short:
                errors.append("密码长度不够")
            if pass_long:
                errors.append("密码过长")
            if name_len:
                errors.append("用户名长度不够")

            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>一个非常酷的CTF竞赛平台,官网: <a href="https://ctfd.io">ctfd.io</a></p>
            <p>关注我们的社交媒体:</p>
            <a href="https://twitter.com/ctfdio"><i class="fab fa-twitter fa-2x" aria-hidden="true"></i></a>&nbsp;
            <a href="https://facebook.com/ctfdio"><i class="fab fa-facebook fa-2x" aria-hidden="true"></i></a>&nbsp;
            <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">点击这里</a> 登录并设置您的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"))
Example #49
0
    def admin_config():
        if request.method == "POST":
            try:
                start = int(request.form['start'])
                end = int(request.form['end'])
            except (ValueError, TypeError):
                start = None
                end = None

            try:
                view_challenges_unregistered = bool(request.form.get('view_challenges_unregistered', None))
                prevent_registration = bool(request.form.get('prevent_registration', None))
            except (ValueError, TypeError):
                view_challenges_unregistered = None
                prevent_registration = None

            ctf_name = set_config("ctf_name", request.form.get('ctf_name', None))
            mg_api_key = set_config("mg_api_key", request.form.get('mg_api_key', None))
            do_api_key = set_config("do_api_key", request.form.get('do_api_key', None))

            db_start = Config.query.filter_by(key='start').first()
            db_start.value = start

            db_end = Config.query.filter_by(key='end').first()
            db_end.value = end

            db_view_challenges_unregistered = Config.query.filter_by(key='view_challenges_unregistered').first()
            db_view_challenges_unregistered.value = view_challenges_unregistered

            db_prevent_registration = Config.query.filter_by(key='prevent_registration').first()
            db_prevent_registration.value = prevent_registration

            db.session.add(db_start)
            db.session.add(db_end)
            db.session.add(db_view_challenges_unregistered)
            db.session.add(db_prevent_registration)

            db.session.commit()
            return redirect('/admin/config')

        ctf_name = get_config('ctf_name')
        if not ctf_name:
            set_config('do_api_key', None)

        mg_api_key = get_config('do_api_key')
        if not mg_api_key:
            set_config('do_api_key', None)

        do_api_key = get_config('do_api_key')
        if not do_api_key:
            set_config('do_api_key', None)

        start = get_config('start')
        if not start:
            set_config('start', None)

        end = get_config('end')
        if not end:
            set_config('end', None)

        view_challenges_unregistered = get_config('view_challenges_unregistered') == '1'
        if not view_challenges_unregistered:
            set_config('view_challenges_unregistered', None)

        prevent_registration = get_config('prevent_registration') == '1'
        if not prevent_registration:
            set_config('prevent_registration', None)

        db.session.commit()
        db.session.close()

        return render_template('admin/config.html', ctf_name=ctf_name, start=start, end=end,
                               view_challenges_unregistered=view_challenges_unregistered,
                               prevent_registration=prevent_registration, do_api_key=do_api_key, mg_api_key=mg_api_key)
Example #50
0
def invite():
    infos = get_infos()
    errors = get_errors()
    code = request.args.get("code")

    if code is None:
        abort(404)

    user = get_current_user_attrs()
    if user.team_id:
        errors.append("You are already in a team. You cannot join another.")

    try:
        team = Teams.load_invite_code(code)
    except TeamTokenExpiredException:
        abort(403, description="This invite URL has expired")
    except TeamTokenInvalidException:
        abort(403, description="This invite URL is invalid")

    team_size_limit = get_config("team_size", default=0)

    if request.method == "GET":
        if team_size_limit:
            infos.append(
                "Teams are limited to {limit} member{plural}".format(
                    limit=team_size_limit, plural=pluralize(number=team_size_limit)
                )
            )

        return render_template(
            "teams/invite.html", team=team, infos=infos, errors=errors
        )

    if request.method == "POST":
        if errors:
            return (
                render_template(
                    "teams/invite.html", team=team, infos=infos, errors=errors
                ),
                403,
            )

        if team_size_limit and len(team.members) >= team_size_limit:
            errors.append(
                "{name} has already reached the team size limit of {limit}".format(
                    name=team.name, limit=team_size_limit
                )
            )
            return (
                render_template(
                    "teams/invite.html", team=team, infos=infos, errors=errors
                ),
                403,
            )

        user = get_current_user()
        user.team_id = team.id
        db.session.commit()

        clear_user_session(user_id=user.id)
        clear_team_session(team_id=team.id)

        return redirect(url_for("challenges.listing"))
Example #51
0
def admin_config():
    if request.method == "POST":
        start = None
        end = None
        if request.form.get('start'):
            start = int(request.form['start'])
        if request.form.get('end'):
            end = int(request.form['end'])
            if end < unix_time(datetime.datetime.now()):
                end = None

        try:
            view_challenges_unregistered = bool(
                request.form.get('view_challenges_unregistered', None))
            view_scoreboard_if_authed = bool(
                request.form.get('view_scoreboard_if_authed', None))
            prevent_registration = bool(
                request.form.get('prevent_registration', None))
            prevent_name_change = bool(
                request.form.get('prevent_name_change', None))
            view_after_ctf = bool(request.form.get('view_after_ctf', None))
            verify_emails = bool(request.form.get('verify_emails', None))
            mail_tls = bool(request.form.get('mail_tls', None))
            mail_ssl = bool(request.form.get('mail_ssl', None))
        except (ValueError, TypeError):
            view_challenges_unregistered = None
            view_scoreboard_if_authed = None
            prevent_registration = None
            prevent_name_change = None
            view_after_ctf = None
            verify_emails = None
            mail_tls = None
            mail_ssl = None
        finally:
            view_challenges_unregistered = set_config(
                'view_challenges_unregistered', view_challenges_unregistered)
            view_scoreboard_if_authed = set_config('view_scoreboard_if_authed',
                                                   view_scoreboard_if_authed)
            prevent_registration = set_config('prevent_registration',
                                              prevent_registration)
            prevent_name_change = set_config('prevent_name_change',
                                             prevent_name_change)
            view_after_ctf = set_config('view_after_ctf', view_after_ctf)
            verify_emails = set_config('verify_emails', verify_emails)
            mail_tls = set_config('mail_tls', mail_tls)
            mail_ssl = set_config('mail_ssl', mail_ssl)

        mail_server = set_config("mail_server",
                                 request.form.get('mail_server', None))
        mail_port = set_config("mail_port",
                               request.form.get('mail_port', None))

        mail_username = set_config("mail_username",
                                   request.form.get('mail_username', None))
        mail_password = set_config("mail_password",
                                   request.form.get('mail_password', None))

        ctf_name = set_config("ctf_name", request.form.get('ctf_name', None))
        ctf_theme = set_config("ctf_theme",
                               request.form.get('ctf_theme', None))

        mg_base_url = set_config("mg_base_url",
                                 request.form.get('mg_base_url', None))
        mg_api_key = set_config("mg_api_key",
                                request.form.get('mg_api_key', None))

        max_tries = set_config("max_tries",
                               request.form.get('max_tries', None))

        db_start = Config.query.filter_by(key='start').first()
        db_start.value = start

        db_end = Config.query.filter_by(key='end').first()
        db_end.value = end

        db.session.add(db_start)
        db.session.add(db_end)

        db.session.commit()
        db.session.close()
        return redirect(url_for('admin.admin_config'))

    ctf_name = get_config('ctf_name')
    ctf_theme = get_config('ctf_theme')
    max_tries = get_config('max_tries')

    mail_server = get_config('mail_server')
    mail_port = get_config('mail_port')
    mail_username = get_config('mail_username')
    mail_password = get_config('mail_password')

    mg_api_key = get_config('mg_api_key')
    mg_base_url = get_config('mg_base_url')
    if not max_tries:
        set_config('max_tries', 0)
        max_tries = 0

    view_after_ctf = get_config('view_after_ctf')
    start = get_config('start')
    end = get_config('end')

    mail_tls = get_config('mail_tls')
    mail_ssl = get_config('mail_ssl')

    view_challenges_unregistered = get_config('view_challenges_unregistered')
    view_scoreboard_if_authed = get_config('view_scoreboard_if_authed')
    prevent_registration = get_config('prevent_registration')
    prevent_name_change = get_config('prevent_name_change')
    verify_emails = get_config('verify_emails')

    db.session.commit()
    db.session.close()

    months = [
        'January', 'February', 'March', 'April', 'May', 'June', 'July',
        'August', 'September', 'October', 'November', 'December'
    ]

    curr_year = datetime.date.today().year
    start_days = 0
    end_days = 0

    if start:
        start = datetime.datetime.fromtimestamp(float(start))
        start_days = calendar.monthrange(start.year, start.month)[1]
    if end:
        end = datetime.datetime.fromtimestamp(float(end))
        end_days = calendar.monthrange(end.year, end.month)[1]

    themes = get_themes()
    themes.remove(ctf_theme)

    return render_template(
        'admin/config.html',
        ctf_name=ctf_name,
        ctf_theme_config=ctf_theme,
        start=start,
        end=end,
        max_tries=max_tries,
        mail_server=mail_server,
        mail_port=mail_port,
        mail_username=mail_username,
        mail_password=mail_password,
        mail_tls=mail_tls,
        mail_ssl=mail_ssl,
        view_challenges_unregistered=view_challenges_unregistered,
        view_scoreboard_if_authed=view_scoreboard_if_authed,
        prevent_registration=prevent_registration,
        mg_base_url=mg_base_url,
        mg_api_key=mg_api_key,
        prevent_name_change=prevent_name_change,
        verify_emails=verify_emails,
        view_after_ctf=view_after_ctf,
        months=months,
        curr_year=curr_year,
        start_days=start_days,
        end_days=end_days,
        themes=themes)
Example #52
0
def new():
    infos = get_infos()
    errors = get_errors()

    if bool(get_config("team_creation", default=True)) is False:
        abort(
            403,
            description="Team creation is currently disabled. Please join an existing team.",
        )

    user = get_current_user_attrs()
    if user.team_id:
        errors.append("You are already in a team. You cannot join another.")

    if request.method == "GET":
        team_size_limit = get_config("team_size", default=0)
        if team_size_limit:
            plural = "" if team_size_limit == 1 else "s"
            infos.append(
                "Teams are limited to {limit} member{plural}".format(
                    limit=team_size_limit, plural=plural
                )
            )
        return render_template("teams/new_team.html", infos=infos, errors=errors)

    elif request.method == "POST":
        teamname = request.form.get("name", "").strip()
        passphrase = request.form.get("password", "").strip()

        website = request.form.get("website")
        affiliation = request.form.get("affiliation")

        user = get_current_user()

        existing_team = Teams.query.filter_by(name=teamname).first()
        if existing_team:
            errors.append("That team name is already taken")
        if not teamname:
            errors.append("That team name is invalid")

        # Process additional user fields
        fields = {}
        for field in TeamFields.query.all():
            fields[field.id] = field

        entries = {}
        for field_id, field in fields.items():
            value = request.form.get(f"fields[{field_id}]", "").strip()
            if field.required is True and (value is None or value == ""):
                errors.append("Please provide all required fields")
                break

            # Handle special casing of existing profile fields
            if field.name.lower() == "affiliation":
                affiliation = value
                break
            elif field.name.lower() == "website":
                website = value
                break

            if field.field_type == "boolean":
                entries[field_id] = bool(value)
            else:
                entries[field_id] = value

        if website:
            valid_website = validators.validate_url(website)
        else:
            valid_website = True

        if affiliation:
            valid_affiliation = len(affiliation) < 128
        else:
            valid_affiliation = True

        if valid_website is False:
            errors.append("Websites must be a proper URL starting with http or https")
        if valid_affiliation is False:
            errors.append("Please provide a shorter affiliation")

        if errors:
            return render_template("teams/new_team.html", errors=errors), 403

        team = Teams(name=teamname, password=passphrase, captain_id=user.id)

        if website:
            team.website = website
        if affiliation:
            team.affiliation = affiliation

        db.session.add(team)
        db.session.commit()

        for field_id, value in entries.items():
            entry = TeamFieldEntries(field_id=field_id, value=value, team_id=team.id)
            db.session.add(entry)
        db.session.commit()

        user.team_id = team.id
        db.session.commit()

        clear_user_session(user_id=user.id)
        clear_team_session(team_id=team.id)

        return redirect(url_for("challenges.listing"))
Example #53
0
def create_app(config='CTFd.config.Config'):
    app = CTFdFlask(__name__)
    with app.app_context():
        app.config.from_object(config)

        theme_loader = ThemeLoader(os.path.join(app.root_path, 'themes'),
                                   followlinks=True)
        app.jinja_loader = theme_loader

        from CTFd.models import db, Teams, Solves, Challenges, Fails, Flags, Tags, Files, Tracking

        url = create_database()

        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
        # This is mostly so we can force MySQL's charset
        app.config['SQLALCHEMY_DATABASE_URI'] = str(url)

        # Register database
        db.init_app(app)

        # Register Flask-Migrate
        migrations.init_app(app, db)

        # Alembic sqlite support is lacking so we should just create_all anyway
        if url.drivername.startswith('sqlite'):
            db.create_all()
            stamp()
        else:
            # This creates tables instead of db.create_all()
            # Allows migrations to happen properly
            upgrade()

        from CTFd.models import ma

        ma.init_app(app)

        app.db = db
        app.VERSION = __version__

        from CTFd.cache import cache

        cache.init_app(app)
        app.cache = cache

        # If you have multiple workers you must have a shared cache
        socketio.init_app(app,
                          async_mode=app.config.get('SOCKETIO_ASYNC_MODE'),
                          message_queue=app.config.get('CACHE_REDIS_URL'))

        if app.config.get('REVERSE_PROXY'):
            app.wsgi_app = ProxyFix(app.wsgi_app)

        version = utils.get_config('ctf_version')

        # Upgrading from an older version of CTFd
        if version and (StrictVersion(version) < StrictVersion(__version__)):
            if confirm_upgrade():
                run_upgrade()
            else:
                exit()

        if not version:
            utils.set_config('ctf_version', __version__)

        if not utils.get_config('ctf_theme'):
            utils.set_config('ctf_theme', 'core')

        update_check(force=True)

        init_request_processors(app)
        init_template_filters(app)
        init_template_globals(app)

        # Importing here allows tests to use sensible names (e.g. api instead of api_bp)
        from CTFd.views import views
        from CTFd.teams import teams
        from CTFd.users import users
        from CTFd.challenges import challenges
        from CTFd.scoreboard import scoreboard
        from CTFd.auth import auth
        from CTFd.admin import admin
        from CTFd.api import api
        from CTFd.events import events
        from CTFd.errors import page_not_found, forbidden, general_error, gateway_error

        app.register_blueprint(views)
        app.register_blueprint(teams)
        app.register_blueprint(users)
        app.register_blueprint(challenges)
        app.register_blueprint(scoreboard)
        app.register_blueprint(auth)
        app.register_blueprint(api)
        app.register_blueprint(events)

        app.register_blueprint(admin)

        app.register_error_handler(404, page_not_found)
        app.register_error_handler(403, forbidden)
        app.register_error_handler(500, general_error)
        app.register_error_handler(502, gateway_error)

        init_logs(app)
        init_plugins(app)

        return app
Example #54
0
 def __require_authentication_if_config(*args, **kwargs):
     value = get_config(config_key)
     if value and current_user.authed():
         return redirect(url_for("auth.login", next=request.full_path))
     else:
         return f(*args, **kwargs)
Example #55
0
def test_user_can_reset_password(mock_smtp):
    '''Test that a user is capable of resetting their password'''
    from email.mime.text import MIMEText
    app = create_ctfd()
    with app.app_context():
        # Set CTFd to send emails
        set_config('mail_server', 'localhost')
        set_config('mail_port', 25)
        set_config('mail_username', 'username')
        set_config('mail_password', 'password')

        # Create a user
        register_user(app, name="user1", email="*****@*****.**")

        with app.test_client() as client:
            r = client.get('/reset_password')

            # Build reset password data
            with client.session_transaction() as sess:
                data = {
                    'nonce': sess.get('nonce'),
                    'email': '*****@*****.**'
                }

            # Issue the password reset request
            r = client.post('/reset_password', data=data)

            from_addr = get_config('mailfrom_addr') or app.config.get('MAILFROM_ADDR')
            to_addr = '*****@*****.**'

            # Build the email
            msg = """
Did you initiate a password reset?

http://localhost/reset_password/InVzZXIxIi5BZktHUGcuTVhkTmZtOWU2U2xwSXZ1MlFwTjdwa3F5V3hR

"""
            email_msg = MIMEText(msg)
            email_msg['Subject'] = "Message from CTFd"
            email_msg['From'] = from_addr
            email_msg['To'] = to_addr

            # Make sure that the reset password email is sent
            mock_smtp.return_value.sendmail.assert_called_with(from_addr, [to_addr], email_msg.as_string())

            # Get user's original password
            team = Teams.query.filter_by(email="*****@*****.**").first()
            team_password_saved = team.password

            # Build the POST data
            with client.session_transaction() as sess:
                data = {
                    'nonce': sess.get('nonce'),
                    'password': '******'
                }

            # Do the password reset
            r = client.get('/reset_password')
            r = client.post('/reset_password/InVzZXIxIi5BZktHUGcuTVhkTmZtOWU2U2xwSXZ1MlFwTjdwa3F5V3hR', data=data)

            # Make sure that the user's password changed
            team = Teams.query.filter_by(email="*****@*****.**").first()
            assert team.password != team_password_saved
    destroy_ctfd(app)
Example #56
0
    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).order_by(Hints.cost).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}
Example #57
0
def scoreboard_view():
    if get_config('view_scoreboard_if_authed') and not authed():
        return redirect(url_for('auth.login', next=request.path))
    standings = get_standings()
    return render_template('scoreboard.html', teams=standings)
Example #58
0
def chal(chalid):
    if not ctftime():
        return redirect(url_for('challenges.challenges_view', _external=True))
    if authed():
        fails = WrongKeys.query.filter_by(teamid=session['id'],
                                          chalid=chalid).count()
        logger = logging.getLogger('keys')
        data = (time.strftime("%m/%d/%Y %X"),
                session['username'].encode('utf-8'),
                request.form['key'].encode('utf-8'), get_kpm(session['id']))
        print("[{0}] {1} submitted {2} with kpm {3}".format(*data))

        # Anti-bruteforce / submitting keys too quickly
        if get_kpm(session['id']) > 10:
            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.warn(
                "[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(
                    *data))
            # return "3" # Submitting too fast
            return jsonify({
                'status':
                '3',
                'message':
                "You're submitting keys too fast. Slow down."
            })

        solves = Solves.query.filter_by(teamid=session['id'],
                                        chalid=chalid).first()

        # Challange not solved yet
        if not solves:
            chal = Challenges.query.filter_by(id=chalid).first()
            key = str(request.form['key'].strip().lower())
            keys = json.loads(chal.flags)

            # Hit max attempts
            max_tries = int(get_config("max_tries"))
            if fails >= max_tries > 0:
                return jsonify({
                    'status': '0',
                    'message': "You have 0 tries remaining"
                })

            for x in keys:
                if x['type'] == 0:  #static key
                    print(x['flag'], key.strip().lower())
                    if x['flag'] and x['flag'].strip().lower() == key.strip(
                    ).lower():
                        solve = Solves(chalid=chalid,
                                       teamid=session['id'],
                                       ip=get_ip(),
                                       flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info(
                            "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".
                            format(*data))
                        # return "1" # key was correct
                        return jsonify({'status': '1', 'message': 'Correct'})
                elif x['type'] == 1:  #regex
                    res = re.match(str(x['flag']), key, re.IGNORECASE)
                    if res and res.group() == key:
                        solve = Solves(chalid=chalid,
                                       teamid=session['id'],
                                       ip=get_ip(),
                                       flag=key)
                        db.session.add(solve)
                        db.session.commit()
                        db.session.close()
                        logger.info(
                            "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".
                            format(*data))
                        # return "1" # key was correct
                        return jsonify({'status': '1', 'message': 'Correct'})

            wrong = WrongKeys(session['id'], chalid, request.form['key'])
            db.session.add(wrong)
            db.session.commit()
            db.session.close()
            logger.info(
                "[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
            # return '0' # key was wrong
            if max_tries:
                attempts_left = max_tries - fails
                tries_str = 'tries'
                if attempts_left == 1:
                    tries_str = 'try'
                return jsonify({
                    'status':
                    '0',
                    'message':
                    'Incorrect. You have {} {} remaining.'.format(
                        attempts_left, tries_str)
                })
            else:
                return jsonify({'status': '0', 'message': 'Incorrect'})

        # Challenge already solved
        else:
            logger.info(
                "{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format(
                    *data))
            # return "2" # challenge was already solved
            return jsonify({
                'status': '2',
                'message': 'You already solved this'
            })
    else:
        return "-1"
Example #59
0
def create_app(config='CTFd.config.Config'):
    app = Flask(__name__)
    with app.app_context():
        app.config.from_object(config)
        app.jinja_loader = ThemeLoader(os.path.join(app.root_path, 'themes'),
                                       followlinks=True)

        from CTFd.models import db, Teams, Solves, Challenges, WrongKeys, Keys, Tags, Files, Tracking

        url = make_url(app.config['SQLALCHEMY_DATABASE_URI'])
        if url.drivername == 'postgres':
            url.drivername = 'postgresql'

        if url.drivername.startswith('mysql'):
            url.query['charset'] = 'utf8mb4'

        # Creates database if the database database does not exist
        if not database_exists(url):
            if url.drivername.startswith('mysql'):
                create_database(url, encoding='utf8mb4')
            else:
                create_database(url)

        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
        # This is mostly so we can force MySQL's charset
        app.config['SQLALCHEMY_DATABASE_URI'] = str(url)

        # Register database
        db.init_app(app)

        # Register Flask-Migrate
        migrate.init_app(app, db)

        # This creates tables instead of db.create_all()
        # Allows migrations to happen properly
        migrate_upgrade()

        # Alembic sqlite support is lacking so we should just create_all anyway
        if url.drivername.startswith('sqlite'):
            db.create_all()

        app.db = db

        cache.init_app(app)
        app.cache = cache

        version = utils.get_config('ctf_version')

        if not version:  # Upgrading from an unversioned CTFd
            utils.set_config('ctf_version', __version__)

        if version and (StrictVersion(version) < StrictVersion(__version__)
                        ):  # Upgrading from an older version of CTFd
            print("/*\\ CTFd has updated and must update the database! /*\\")
            print("/*\\ Please backup your database before proceeding! /*\\")
            print(
                "/*\\ CTFd maintainers are not responsible for any data loss! /*\\"
            )
            if input('Run database migrations (Y/N)').lower().strip() == 'y':
                migrate_stamp()
                migrate_upgrade()
                utils.set_config('ctf_version', __version__)
            else:
                print('/*\\ Ignored database migrations... /*\\')
                exit()

        if not utils.get_config('ctf_theme'):
            utils.set_config('ctf_theme', 'original')

        from CTFd.views import views
        from CTFd.challenges import challenges
        from CTFd.scoreboard import scoreboard
        from CTFd.auth import auth
        from CTFd.admin import admin, admin_statistics, admin_challenges, admin_pages, admin_scoreboard, admin_containers, admin_keys, admin_teams
        from CTFd.utils import init_utils, init_errors, init_logs

        init_utils(app)
        init_errors(app)
        init_logs(app)

        app.register_blueprint(views)
        app.register_blueprint(challenges)
        app.register_blueprint(scoreboard)
        app.register_blueprint(auth)

        app.register_blueprint(admin)
        app.register_blueprint(admin_statistics)
        app.register_blueprint(admin_challenges)
        app.register_blueprint(admin_teams)
        app.register_blueprint(admin_scoreboard)
        app.register_blueprint(admin_keys)
        app.register_blueprint(admin_containers)
        app.register_blueprint(admin_pages)

        from CTFd.plugins import init_plugins

        init_plugins(app)

        return app
Example #60
0
def files(path):
    """
    Route in charge of dealing with making sure that CTF challenges are only accessible during the competition.
    :param path:
    :return:
    """
    f = Files.query.filter_by(location=path).first_or_404()
    if f.type == "challenge":
        if challenges_visible():
            if current_user.is_admin() is False:
                if not ctftime():
                    if ctf_ended() and view_after_ctf():
                        pass
                    else:
                        abort(403)
        else:
            if not ctftime():
                abort(403)

            # Allow downloads if a valid token is provided
            token = request.args.get("token", "")
            try:
                data = unserialize(token, max_age=3600)
                user_id = data.get("user_id")
                team_id = data.get("team_id")
                file_id = data.get("file_id")
                user = Users.query.filter_by(id=user_id).first()
                team = Teams.query.filter_by(id=team_id).first()

                # Check user is admin if challenge_visibility is admins only
                if (
                    get_config("challenge_visibility") == "admins"
                    and user.type != "admin"
                ):
                    abort(403)

                # Check that the user exists and isn't banned
                if user:
                    if user.banned:
                        abort(403)
                else:
                    abort(403)

                # Check that the team isn't banned
                if team:
                    if team.banned:
                        abort(403)
                else:
                    pass

                # Check that the token properly refers to the file
                if file_id != f.id:
                    abort(403)

            # The token isn't expired or broken
            except (BadTimeSignature, SignatureExpired, BadSignature):
                abort(403)

    uploader = get_uploader()
    try:
        return uploader.download(f.location)
    except IOError:
        abort(404)