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'))
def file_handler(path): f = Files.query.filter_by(location=path).first_or_404() if f.chal: if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf() and utils.ctf_started(): pass else: abort(403) upload_folder = os.path.join(app.root_path, app.config['UPLOAD_FOLDER']) return send_file(safe_join(upload_folder, f.location))
def hints_view(hintid): if not utils.ctf_started(): abort(403) hint = Hints.query.filter_by(id=hintid).first_or_404() chal = Challenges.query.filter_by(id=hint.chal).first() unlock = Unlocks.query.filter_by(model='hints', itemid=hintid, teamid=session['id']).first() if request.method == 'GET': if unlock: return jsonify({ 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost }) else: return jsonify({'chal': hint.chal, 'cost': hint.cost}) elif request.method == 'POST': if not unlock and utils.ctftime(): team = Teams.query.filter_by(id=session['id']).first() if team.score() < hint.cost: return jsonify({'errors': 'Not enough points'}) unlock = Unlocks(model='hints', teamid=session['id'], itemid=hint.id) award = Awards(teamid=session['id'], name=text_type('Hint for {}'.format(chal.name)), value=(-hint.cost)) db.session.add(unlock) db.session.add(award) db.session.commit() json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data) elif utils.ctf_ended(): json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data) else: json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data)
def user_can_get_config(): if utils.is_admin(): return True if not (utils.authed() and utils.is_verified()): return False if not utils.user_can_view_challenges(): return False if not (utils.ctf_started() and (utils.ctf_ended() or utils.view_after_ctf())): return False return True
def hints_view(hintid): if utils.ctf_started() is False: if utils.is_admin() is False: abort(403) hint = Hints.query.filter_by(id=hintid).first_or_404() chal = Challenges.query.filter_by(id=hint.chal).first() unlock = Unlocks.query.filter_by(model='hints', itemid=hintid, teamid=session['id']).first() if request.method == 'GET': if unlock: return jsonify({ 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost }) else: return jsonify({'chal': hint.chal, 'cost': hint.cost}) elif request.method == 'POST': if unlock is None: # The user does not have an unlock. if utils.ctftime() or ( utils.ctf_ended() and utils.view_after_ctf()) or utils.is_admin() is True: # It's ctftime or the CTF has ended (but we allow views after) team = Teams.query.filter_by(id=session['id']).first() if team.score() < hint.cost: return jsonify({'errors': 'Not enough points'}) unlock = Unlocks(model='hints', teamid=session['id'], itemid=hint.id) award = Awards(teamid=session['id'], name=text_type('Hint for {}'.format(chal.name)), value=(-hint.cost)) db.session.add(unlock) db.session.add(award) db.session.commit() json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data) elif utils.ctf_ended(): # The CTF has ended. No views after. abort(403) else: # The user does have an unlock, we should give them their hint. json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data)
def test_ctf_started(): '''Tests that the ctf_started function returns the correct value''' app = create_ctfd() with app.app_context(): assert ctf_started() == True set_config('start', '1507089600' ) # Wednesday, October 4, 2017 12:00:00 AM GMT-04:00 DST set_config( 'end', '1507262400') # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST with freeze_time("2017-10-3"): assert ctf_started() == False with freeze_time("2017-10-5"): assert ctf_started() == True with freeze_time("2017-10-7"): assert ctf_started() == True destroy_ctfd(app)
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'): if utils.authed(): if utils.is_admin() is False and utils.is_verified( ) is False: # 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'))
def hints_view(hintid): if not utils.ctf_started(): abort(403) hint = Hints.query.filter_by(id=hintid).first_or_404() chal = Challenges.query.filter_by(id=hint.chal).first() unlock = Unlocks.query.filter_by(model='hints', itemid=hintid, teamid=session['id']).first() if request.method == 'GET': if unlock: return jsonify({ 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost }) else: return jsonify({ 'chal': hint.chal, 'cost': hint.cost }) elif request.method == 'POST': if not unlock and utils.ctftime(): team = Teams.query.filter_by(id=session['id']).first() if team.score() < hint.cost: return jsonify({'errors': 'Not enough points'}) unlock = Unlocks(model='hints', teamid=session['id'], itemid=hint.id) award = Awards(teamid=session['id'], name='Hint for {}'.format(chal.name), value=(-hint.cost)) db.session.add(unlock) db.session.add(award) db.session.commit() json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data) elif utils.ctf_ended(): json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data) else: json_data = { 'hint': hint.hint, 'chal': hint.chal, 'cost': hint.cost } db.session.close() return jsonify(json_data)
def during_ctf_time_only_wrapper(*args, **kwargs): if utils.ctftime() or utils.is_admin(): return f(*args, **kwargs) else: if utils.ctf_ended(): if utils.view_after_ctf(): return f(*args, **kwargs) else: error = '{} has ended'.format(utils.ctf_name()) abort(403, description=error) if utils.ctf_started() is False: error = '{} has not started yet'.format(utils.ctf_name()) abort(403, description=error)
def chals(): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: abort(403) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): chals = Challenges.query.filter(or_(Challenges.hidden != True, Challenges.hidden == None)).order_by(Challenges.value).all() # Only one line in chals() needed to add for Challenge Discovery # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- chals = discovery(chals) if len(chals)!=0 else chals # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- json = {'game': []} for x in chals: tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=x.id).all()] files = [str(f.location) for f in Files.query.filter_by(chal=x.id).all()] unlocked_hints = [] if utils.authed(): unlocked_hints = set([u.itemid for u in Unlocks.query.filter_by(model='hints', teamid=session['id'])]) hints = [] for hint in Hints.query.filter_by(chal=x.id).all(): if hint.id in unlocked_hints or utils.ctf_ended(): hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint}) else: hints.append({'id': hint.id, 'cost': hint.cost}) chal_type = get_chal_class(x.type) json['game'].append({ 'id': x.id, 'type': chal_type.name, 'name': x.name, 'value': x.value, 'description': x.description, 'category': x.category, 'files': files, 'tags': tags, 'hints': hints, 'template': chal_type.templates['modal'], 'script': chal_type.scripts['modal'], }) db.session.close() return jsonify(json) else: db.session.close() abort(403)
def chals(): if not is_admin(): if not ctftime(): if view_after_ctf(): pass else: return redirect(url_for('views.index')) if user_can_view_challenges() and (ctf_started() or is_admin()): chals = Challenges.query.filter( or_(Challenges.hidden != True, Challenges.hidden == None)).order_by(Challenges.value).all() json = {'game': []} for chal in chals: tags = [ tag.tag for tag in Tags.query.filter_by(chal=chal.id).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=chal.id).all() ] hints = [{ 'title': hint.title, 'description': hint.description } for hint in Announcements.query.filter_by( chalid=chal.id).order_by(Announcements.date.asc()).all()] notepad = Notepads.query.filter_by(teamid=session['id'], chalid=chal.id).first() notepad = notepad.content if notepad else '' json['game'].append({ 'id': chal.id, 'name': chal.name, 'value': chal.value, 'description': chal.description, 'category': chal.category, 'down': chal.down, 'files': files, 'tags': tags, 'hints': hints, 'notepad': notepad, }) db.session.close() return jsonify(json) else: db.session.close() return redirect(url_for('auth.login', next='chals'))
def challenges_view(): errors = [] start = get_config('start') or 0 end = get_config('end') or 0 if not is_admin(): # User is not an admin if not ctftime(): # It is not CTF time if start > time.time( ): # We are NOT allowed to view after the CTF ends errors.append('{} challenges will be posted soon!'.format( ctf_name())) elif not view_after_ctf(): errors.append('{} has ended.'.format(ctf_name())) return render_template('chals.html', errors=errors, start=int(start), end=int(end)) if get_config('verify_emails' ) and not is_verified(): # User is not confirmed return redirect(url_for('auth.confirm_user')) if user_can_view_challenges(): # Do we allow unauthenticated users? if get_config('start') and not ctf_started(): errors.append('{} has not started yet'.format(ctf_name())) if (get_config('end') and ctf_ended()) and not view_after_ctf(): errors.append('{} has ended'.format(ctf_name())) if not is_on_team(): errors.append('You are not on a team!') return render_template('chals.html', errors=errors, start=int(start), end=int(end)) else: if not is_on_team(): errors.append( 'You must create or join a team before you can start playing') return render_template('chals.html', errors=errors, start=int(start), end=int(end)) return redirect(url_for('auth.login', next='challenges'))
def challenges_view(): infos = [] errors = [] start = utils.get_config('start') or 0 end = utils.get_config('end') or 0 if utils.ctf_paused(): infos.append('{} is paused'.format(utils.ctf_name())) 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('challenges.html', infos=infos, errors=errors, start=int(start), end=int(end)) return render_template('challenges.html', infos=infos, errors=errors, start=int(start), end=int(end))
def chals(): if not is_admin(): if not ctftime(): if view_after_ctf(): pass else: return redirect(url_for('views.static_html')) if user_can_view_challenges() and (ctf_started() or is_admin()): chals = Challenges.query.filter( or_(Challenges.hidden != True, Challenges.hidden == None)).add_columns( 'id', 'name', 'value', 'description', 'category').order_by(Challenges.value).all() json = {'game': []} for x in chals: tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( chal=x[1]).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=x.id).all() ] json['game'].append({ 'id': x[1], 'name': x[2], 'value': x[3], 'description': x[4], 'category': x[5], 'files': files, 'tags': tags }) db.session.close() return jsonify(json) else: db.session.close() return redirect(url_for('auth.login', next='chals'))
def chals(): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: return redirect(url_for('views.static_html')) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): chals = Challenges.query.filter(or_(Challenges.hidden != True, Challenges.hidden == None)).order_by(Challenges.value).all() json = {'game': []} for x in chals: tags = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=x.id).all()] files = [str(f.location) for f in Files.query.filter_by(chal=x.id).all()] unlocked_hints = set([u.itemid for u in Unlocks.query.filter_by(model='hints', teamid=session['id'])]) hints = [] for hint in Hints.query.filter_by(chal=x.id).all(): if hint.id in unlocked_hints or utils.ctf_ended(): hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint}) else: hints.append({'id': hint.id, 'cost': hint.cost}) # hints = [{'id':hint.id, 'cost':hint.cost} for hint in Hints.query.filter_by(chal=x.id).all()] chal_type = get_chal_class(x.type) json['game'].append({ 'id': x.id, 'type': chal_type.name, 'name': x.name, 'value': x.value, 'description': x.description, 'category': x.category, 'files': files, 'tags': tags, 'hints': hints }) db.session.close() return jsonify(json) else: db.session.close() return redirect(url_for('auth.login', next='chals'))
def file_handler(path): f = Files.query.filter_by(location=path).first_or_404() if f.chal: if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf() and utils.ctf_started(): pass else: abort(403) upload_folder = os.path.join(app.root_path, app.config['UPLOAD_FOLDER']) if not f.dynamic: return send_file(safe_join(upload_folder, f.location)) filedir_path = safe_join(upload_folder, f.location.split('/')[0]) m = hashlib.md5() m.update(session['token']) teamfile_name = m.hexdigest() + ".zip" teamfile_path = safe_join(filedir_path, teamfile_name) if not os.path.exists(teamfile_path): os.system("python %s %s %s %s" % (safe_join(upload_folder, f.location), filedir_path, session['token'], app.root_path)) return send_file(teamfile_path, cache_timeout=0)
def competitions(): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: abort(403) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): competitions = Competitions.query.all() json = {'competitions': []} for x in competitions: json['competitions'].append({ 'id': x.id, 'title': x.title, 'description': x.description }) db.session.close() return jsonify(json) else: db.session.close() abort(403)
def chals(): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass elif utils.ctf_paused(): abort(make_response('{} paused'.format(utils.ctf_name()), 503)) else: abort( make_response( 'These are not the challenges you are looking for', 403)) if utils.get_config('verify_emails'): if utils.authed(): if utils.is_admin() is False and utils.is_verified( ) is False: # User is not confirmed abort(403) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): teamid = session.get('id') chals = Challenges.query.filter( or_(Challenges.hidden != True, Challenges.hidden == None)).order_by(Challenges.value).all() json = {'game': []} for x in chals: tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( chal=x.id).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=x.id).all() ] unlocked_hints = set([ u.itemid for u in Unlocks.query.filter_by(model='hints', teamid=teamid) ]) hints = [] for hint in Hints.query.filter_by(chal=x.id).all(): if hint.id in unlocked_hints or utils.ctf_ended(): hints.append({ 'id': hint.id, 'cost': hint.cost, 'hint': hint.hint }) else: hints.append({'id': hint.id, 'cost': hint.cost}) chal_type = get_chal_class(x.type) json['game'].append({ 'id': x.id, 'type': chal_type.name, 'name': x.name, 'value': x.value, 'description': x.description, 'category': x.category, 'files': files, 'tags': tags, 'hints': hints, 'template': chal_type.templates['modal'], 'script': chal_type.scripts['modal'], }) db.session.close() return jsonify(json) else: db.session.close() abort(403)
def chal(chalid): if utils.ctf_paused(): return jsonify({ 'status': 3, 'message': '{} is paused'.format(utils.ctf_name()) }) if (utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf())) or utils.is_admin(): team = Teams.query.filter_by(id=session['id']).first() 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) chal = Challenges.query.filter_by(id=chalid).first_or_404() if chal.hidden: abort(404) chal_class = get_chal_class(chal.type) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): chal_class.fail(team=team, chal=chal, request=request) 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: provided_key = request.form['key'].strip() saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': 0, 'message': "You have 0 tries remaining" }) chal_name = chal.name chal_value = chal.value team_id = team.id status, message = chal_class.attempt(chal, request) if status: # The challenge plugin says the input is right if utils.ctftime() or utils.is_admin(): chal_class.solve(team=team, chal=chal, request=request) logger.info( "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format( *data)) team_url = url_for("views.team", teamid=team_id, _external=True) chal_url = url_for("challenges.challenges_view", _anchor=chal_name, _external=True) description = ":white_check_mark: [{0}]({1}) solved [{2}]({3}) ({4})".format( session['username'].encode('utf-8'), team_url, chal_name, chal_url, chal_value) embeds = [{ "description": description, "color": 10553667, "timestamp": datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%SZ') }] utils.send_discord_webhook(embeds) return jsonify({'status': 1, 'message': message}) else: # The challenge plugin says the input is wrong if utils.ctftime() or utils.is_admin(): chal_class.fail(team=team, chal=chal, request=request) 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 - 1 # Off by one since fails has changed since it was gotten tries_str = 'tries' if attempts_left == 1: tries_str = 'try' if message[ -1] not in '!().;?[]\{\}': # Add a punctuation mark if there isn't one message = message + '.' return jsonify({ 'status': 0, 'message': '{} You have {} {} remaining.'.format( message, attempts_left, tries_str) }) else: return jsonify({'status': 0, 'message': message}) # 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 jsonify({ 'status': -1, 'message': "You must be logged in to solve a challenge" })
def comp_challenges(compid): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: abort(403) if utils.get_config('verify_emails'): if utils.authed(): if utils.is_admin() is False and utils.is_verified( ) is False: # User is not confirmed abort(403) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): teamid = session.get('id') comp = Competitions.query.filter(Competitions.id == compid).first() if comp is None: abort(403) if comp.startTime > datetime.datetime.utcnow(): abort(403) #chals=comp.chals json = { 'competition': { 'id': comp.id, 'title': comp.title, 'description': comp.description, 'startTime': comp.startTime, 'endTime': comp.endTime }, 'game': [] } for x in comp.chals: chal = Challenges.query.filter(Challenges.id == x.chalid).first() if chal is None: abort(502) tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter( Tags.chal == chal.id).all() ] files = [ str(f.location) for f in Files.query.filter(Files.chal == chal.id).all() ] unlocked_hints = set([ u.itemid for u in Unlocks.query.filter( Unlocks.model == 'hints', Unlocks.teamid == teamid) ]) hints = [] for hint in Hints.query.filter(Hints.chal == chal.id).all(): if hint.id in unlocked_hints or utils.ctf_ended(): hints.append({ 'id': hint.id, 'cost': hint.cost, 'hint': hint.hint }) else: hints.append({'id': hint.id, 'cost': hint.cost}) chal_type = get_chal_class(chal.type) json['game'].append({ 'id': chal.id, 'type': chal_type.name, 'name': chal.name, 'value': chal.value, 'description': chal.description, 'category': chal.category, 'files': files, 'tags': tags, 'hints': hints, 'template': chal_type.templates['modal'], 'script': chal_type.scripts['modal'], }) db.session.close() return jsonify(json) else: db.session.close() abort(403)
def challenges_view(compid): infos = [] errors = [] start = utils.get_config('start') or 0 end = utils.get_config('end') or 0 comp = Competitions.query.filter(Competitions.id == compid).first() if utils.ctf_paused(): infos.append('{} is paused'.format(utils.ctf_name())) 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())) if comp is None: errors.append('no such competition') start = False if comp.startTime > datetime.datetime.utcnow(): errors = append('{} 尚未开始,敬请期待'.format(comp.title)) start = False return render_template('comp_challenges.html', infos=infos, errors=errors, start=int(start), end=int(end), comp=comp) if utils.get_config('verify_emails'): if utils.authed(): if utils.is_admin() is False and utils.is_verified( ) is False: # 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())) if comp is None: errors.append('no such competition') start = False if comp.startTime > datetime.datetime.utcnow(): errors.append('{} 尚未开始,敬请期待'.format(comp.title)) start = False return render_template('comp_challenges.html', infos=infos, errors=errors, start=int(start), end=int(end), comp=comp) else: return redirect(url_for('auth.login', next=request.path))
def chal(chalid): if utils.ctf_paused(): return jsonify({ 'status': 3, 'message': '{} is paused'.format(utils.ctf_name()) }) if (utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf())) or utils.is_admin(): team = Teams.query.filter_by(id=session['id']).first() 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) chal = Challenges.query.filter_by(id=chalid).first_or_404() if chal.hidden: abort(404) chal_class = get_chal_class(chal.type) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): chal_class.fail(team=team, chal=chal, request=request) logger.warn("[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(*data)) # return '3' # Submitting too fast return jsonify({'status': 3, 'message': "你提交答案过快,请稍后再试"}) solves = Solves.query.filter_by(teamid=session['id'], chalid=chalid).first() # Challange not solved yet if not solves: provided_key = request.form['key'].strip() saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': 0, 'message': "你已经没有机会再试了" }) status, message = chal_class.attempt(chal, request) if status: # The challenge plugin says the input is right if utils.ctftime() or utils.is_admin(): chal_class.solve(team=team, chal=chal, request=request) logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data)) return jsonify({'status': 1, 'message': message}) else: # The challenge plugin says the input is wrong if utils.ctftime() or utils.is_admin(): chal_class.fail(team=team, chal=chal, request=request) 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 - 1 # Off by one since fails has changed since it was gotten tries_str = 'tries' if attempts_left == 1: tries_str = 'try' if message[-1] not in '!().;?[]\{\}': # Add a punctuation mark if there isn't one message = message + '.' return jsonify({'status': 0, 'message': '{} You have {} {} remaining.'.format(message, attempts_left, tries_str)}) else: return jsonify({'status': 0, 'message': message}) # 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': '你已经做过本题了'}) else: return jsonify({ 'status': -1, 'message': "必须先登陆才能解题" })
def chals(chalid=None): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: return redirect(url_for('views.static_html')) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): json = {'game': [], 'nonce': ''} if chalid is None: chals = Challenges.query.order_by(Challenges.value).all() for x in chals: tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( chal=x.id).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=x.id).all() ] chal_type = get_chal_class(x.type) if x.hidden: json['game'].append({ 'id': x.id, 'name': x.name, 'value': x.value, 'category': x.category, 'hidden': True }) else: file_data = [] for i in range(len(files)): file_data.append( [str(files[i]).split('/')[1], str(files[i])]) json['game'].append({ 'id': x.id, 'type': chal_type.name, 'name': x.name, 'value': x.value, 'description': x.description, 'category': x.category, 'files': file_data, 'tags': tags, 'hint': x.hint }) json["nonce"] = session["nonce"] else: chal = Challenges.query.filter_by(id=chalid).all()[0] if chal is None or chal.hidden: json = {"locked": True} else: tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( chal=chal.id).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=chal.id).all() ] chal_type = get_chal_class(chal.type) json = { 'id': chal.id, 'type': chal_type.name, 'name': chal.name, 'value': chal.value, 'description': chal.description, 'category': chal.category, 'files': files, 'tags': tags, 'hint': chal.hint } db.session.close() return jsonify(json) else: db.session.close() return redirect(url_for('auth.login', next='chals'))
def chal(chalid): if utils.ctf_ended() and not utils.view_after_ctf(): return redirect(url_for('challenges.challenges_view')) if not utils.user_can_view_challenges(): return redirect(url_for('auth.login', next=request.path)) if utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf()): 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): 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_or_404() provided_key = unicode(request.form['key'].strip()) saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': '0', 'message': "You have 0 tries remaining" }) chal_class = get_chal_class(chal.type) if chal_class.solve(chal, provided_key): if utils.ctftime(): solve = Solves(chalid=chalid, teamid=session['id'], ip=utils.get_ip(), flag=provided_key) db.session.add(solve) db.session.commit() db.session.close() logger.info( "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format( *data)) return jsonify({'status': '1', 'message': 'Correct'}) if utils.ctftime(): wrong = WrongKeys(teamid=session['id'], chalid=chalid, flag=provided_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 - 1 ## Off by one since fails has changed since it was gotten 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 jsonify({ 'status': '-1', 'message': "You must be logged in to solve a challenge" })
def chals(): if not utils.is_admin(): if not utils.ctftime(): if utils.view_after_ctf(): pass else: abort(403) if utils.get_config('verify_emails'): if utils.authed(): if utils.is_admin() is False and utils.is_verified( ) is False: # User is not confirmed abort(403) if utils.user_can_view_challenges() and (utils.ctf_started() or utils.is_admin()): teamid = session.get('id') chals = Challenges.query.filter( or_(Challenges.hidden != True, Challenges.hidden == None)).order_by( Challenges.value).all() json = {'game': []} for x in chals: tags = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( chal=x.id).all() ] files = [ str(f.location) for f in Files.query.filter_by(chal=x.id).all() ] unlocked_hints = set([ u.itemid for u in Unlocks.query.filter_by(model='hints', teamid=teamid) ]) hints = [] for hint in Hints.query.filter_by(chal=x.id).all(): if hint.id in unlocked_hints or utils.ctf_ended(): hints.append({ 'id': hint.id, 'cost': hint.cost, 'hint': hint.hint }) else: hints.append({'id': hint.id, 'cost': hint.cost}) chal_type = get_chal_class(x.type) if chal_type == CommunityChallenge: owner_id = CommunityChallengeModel.query.filter( CommunityChallengeModel.id == x.id).first().owner else: owner_id = 1 owner = Teams.query.filter(Teams.id == owner_id).first().name own = (owner_id == session['id']) chal_data = { 'id': x.id, 'type': chal_type.name, 'name': x.name, 'value': x.value, 'description': x.description, 'category': x.category, 'files': files, 'tags': tags, 'hints': hints, 'owner': owner, 'own': own, 'template': chal_type.templates['modal'], 'script': chal_type.scripts['modal'] } if own == True: chal_data.update({'nonce': session.get('nonce')}) json['game'].append(chal_data) db.session.close() return jsonify(json) else: db.session.close() abort(403)
def anonchal(): if utils.ctf_paused(): return jsonify({ 'status': 3, 'message': '{} is paused'.format(utils.ctf_name()) }) if utils.is_admin() or (utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf())): team = Teams.query.filter_by(id=session['id']).first() provided_key = request.form['key'].strip() logger = logging.getLogger('keys') data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), provided_key.encode('utf-8'), utils.get_kpm(session['id'])) # Anti-bruteforce / KPM is based on last failed key (not logged), so sleep instead. time.sleep(2) # Find challenge by looking up the provided flag key = db.session.query(Keys).\ join(AnonymousChallenge).\ filter(Keys.flag == provided_key).first() if not key: logger.info( "[{0}] {1} submitted {2} with kpm {3} [WRONG]".format( *data)) return jsonify({'status': 0, 'message': 'Invalid Flag'}) chal = AnonymousChallenge.query.filter_by(id=key.chal).first() chal_class = get_chal_class(chal.type) solves = Solves.query.filter_by(teamid=session['id'], chalid=chal.id).first() # If team hasn't solved challenge yet, save the solve if not solves: # We already know the flag is correct because we checked it already chal_class.solve(team=team, chal=chal, request=request) logger.info( "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format( *data)) return jsonify({'status': 1, 'message': "Correct"}) # Otherwise, raise an error else: logger.info( "{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format( *data)) return jsonify({ 'status': 2, 'message': 'You already solved this' }) else: return jsonify({ 'status': -1, 'message': "You must be logged in to solve a challenge" })
def chal_custom(chalid): if utils.ctf_paused(): return jsonify({ 'status': 3, 'message': '{} is paused'.format(utils.ctf_name()) }) if (utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf())) or utils.is_admin(): team = Teams.query.filter_by(id=session['id']).first() 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) chal = Challenges.query.filter_by(id=chalid).first_or_404() if chal.hidden: abort(404) chal_class = get_chal_class(chal.type) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): chal_class.fail(team=team, chal=chal, request=request) 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: provided_key = request.form['key'].strip() saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': 0, 'message': "You have 0 tries remaining" }) status, message = chal_class.attempt(chal, request) if status: # The challenge plugin says the input is right if utils.ctftime() or utils.is_admin(): chal_class.solve(team=team, chal=chal, request=request) logger.info( "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format( *data)) if "SmartCity" in str(chal_class): print(session['id']) smart_color = SmartCityTeam.query.filter_by( teamId=session['id']).first().color smart_buildingId = SmartCityChallenge.query.filter_by( id=chalid).first().buildingId smart_soundId = SmartCityChallenge.query.filter_by( id=chalid).first().soundId smart_image = SmartCityTeam.query.filter_by( teamId=session['id']).first().image b = smart_buildingId.split('\', \'') b[0] = b[0][2:] b[-1] = b[-1][:-2] print("Team with color " + str(smart_color) + " and image " + str(smart_image) + " solved challenege with buildingId " + str(b) + "and sound " + smart_soundId) smartSession = SmartTable(b, smart_color, smart_image, smart_soundId) createSmartCityTableSession2(smartSession) return jsonify({'status': 1, 'message': message}) else: # The challenge plugin says the input is wrong if utils.ctftime() or utils.is_admin(): chal_class.fail(team=team, chal=chal, request=request) 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 - 1 # Off by one since fails has changed since it was gotten tries_str = 'tries' if attempts_left == 1: tries_str = 'try' if message[ -1] not in '!().;?[]\{\}': # Add a punctuation mark if there isn't one message = message + '.' return jsonify({ 'status': 0, 'message': '{} You have {} {} remaining.'.format( message, attempts_left, tries_str) }) else: return jsonify({'status': 0, 'message': message}) # 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 jsonify({ 'status': -1, 'message': "You must be logged in to solve a challenge" })
def chal(chalid): if ctf_ended() and not view_after_ctf(): return redirect(url_for('challenges.challenges_view')) if not user_can_view_challenges(): return redirect(url_for('auth.login', next=request.path)) if authed() and is_verified() and (ctf_started() or view_after_ctf()): 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: if ctftime(): 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 = unicode(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(): if ctftime(): 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(x['flag'], key, re.IGNORECASE) if res and res.group() == key: if ctftime(): 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'}) if ctftime(): 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"
def chal(chalid): if utils.ctf_ended() and not utils.view_after_ctf(): abort(403) if not utils.user_can_view_challenges(): return redirect(url_for('auth.login', next=request.path)) if (utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf())) or utils.is_admin(): team = Teams.query.filter_by(id=session['id']).first() 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): wrong = WrongKeys(teamid=session['id'], chalid=chalid, ip=utils.get_ip(), flag=request.form['key'].strip()) 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_or_404() provided_key = request.form['key'].strip() saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': 0, 'message': "You have 0 tries remaining" }) chal_class = get_chal_class(chal.type) status, message = chal_class.attempt(chal, request) if status: # The challenge plugin says the input is right if utils.ctftime() or utils.is_admin(): chal_class.solve(team=team, chal=chal, request=request) logger.info( "[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format( *data)) return jsonify({'status': 1, 'message': message}) else: # The challenge plugin says the input is wrong if utils.ctftime() or utils.is_admin(): chal_class.fail(team=team, chal=chal, request=request) 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 - 1 # Off by one since fails has changed since it was gotten tries_str = 'tries' if attempts_left == 1: tries_str = 'try' if message[ -1] not in '!().;?[]\{\}': # Add a punctuation mark if there isn't one message = message + '.' return jsonify({ 'status': 0, 'message': '{} You have {} {} remaining.'.format( message, attempts_left, tries_str) }) else: return jsonify({'status': 0, 'message': message}) # 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 jsonify({ 'status': -1, 'message': "You must be logged in to solve a challenge" })
def chal(chalid): if utils.ctf_ended() and not utils.view_after_ctf(): return redirect(url_for('challenges.challenges_view')) if not utils.user_can_view_challenges(): return redirect(url_for('auth.login', next=request.path)) if utils.authed() and utils.is_verified() and (utils.ctf_started() or utils.view_after_ctf()): 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'), utils.get_kpm(session['id'])) print("[{0}] {1} submitted {2} with kpm {3}".format(*data)) # Anti-bruteforce / submitting keys too quickly if utils.get_kpm(session['id']) > 10: if utils.ctftime(): 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_or_404() provided_key = request.form['key'].strip() saved_keys = Keys.query.filter_by(chal=chal.id).all() # Hit max attempts max_tries = chal.max_attempts if max_tries and fails >= max_tries > 0: return jsonify({ 'status': 0, 'message': "You have 0 tries remaining" }) chal_class = get_chal_class(chal.type) if chal_class.solve(chal, provided_key): if utils.ctftime(): solve = Solves(chalid=chalid, teamid=session['id'], ip=utils.get_ip(), flag=provided_key) db.session.add(solve) db.session.commit() db.session.close() logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data)) return jsonify({'status': 1, 'message': 'Correct'}) if utils.ctftime(): wrong = WrongKeys(teamid=session['id'], chalid=chalid, flag=provided_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 - 1 # Off by one since fails has changed since it was gotten 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 jsonify({ 'status': -1, 'message': "You must be logged in to solve a challenge" })