def test_ctf_ended(): """ Tests that the ctf_ended function returns the correct value """ app = create_ctfd() with app.app_context(): assert ctf_ended() is False with ctftime.init(): with ctftime.not_started(): assert ctf_ended() is False with ctftime.started(): assert ctf_ended() is False with ctftime.ended(): assert ctf_ended() is True destroy_ctfd(app)
def during_ctf_time_only_wrapper(*args, **kwargs): if ctftime() or current_user.is_admin(): return f(*args, **kwargs) else: if ctf_ended(): error = '{} has ended'.format(config.ctf_name()) abort(403, description=error) if ctf_started() is False: error = '{} has not started yet'.format(config.ctf_name()) abort(403, description=error)
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) tags = [ tag['value'] for tag in TagSchema( "user", many=True).dump( chal.tags).data] files = [f.location for f in chal.files] unlocked_hints = set() hints = [] if authed(): user = get_current_user() unlocked_hints = set([u.target for u in HintUnlocks.query.filter_by( type='hints', account_id=user.account_id)]) for hint in Hints.query.filter_by(challenge_id=chal.id).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)\ .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 }
def during_ctf_time_only_wrapper(*args, **kwargs): if ctftime() or current_user.is_admin(): return f(*args, **kwargs) else: if ctf_ended(): if view_after_ctf(): return f(*args, **kwargs) else: error = '{} 已经结束'.format(config.ctf_name()) abort(403, description=error) if ctf_started() is False: error = '{} 尚未开始'.format(config.ctf_name()) abort(403, description=error)
def test_ctf_ended(): """ Tests that the ctf_ended function returns the correct value """ app = create_ctfd() with app.app_context(): assert ctf_ended() is False 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_ended() is False with freeze_time("2017-10-5"): assert ctf_ended() is False with freeze_time("2017-10-7"): assert ctf_ended() is True destroy_ctfd(app)
def listing(): infos = get_infos() errors = get_errors() if ctf_started() is False: errors.append(f"{config.ctf_name()} has not started yet") if ctf_paused() is True: infos.append(f"{config.ctf_name()} is paused") if ctf_ended() is True: infos.append(f"{config.ctf_name()} has ended") return render_template("challenges.html", infos=infos, errors=errors)
def listing(): infos = get_infos() errors = get_errors() if ctf_started() is False: errors.append(f"{config.ctf_name()} ещё не начался") if ctf_paused() is True: infos.append(f"{config.ctf_name()} приостановлен") if ctf_ended() is True: infos.append(f"{config.ctf_name()} закончился") return render_template("challenges.html", infos=infos, errors=errors)
def listing(): infos = get_infos() errors = get_errors() start = get_config("start") or 0 end = get_config("end") or 0 if ctf_paused(): infos.append("{} is paused".format(config.ctf_name())) # CTF has ended but we want to allow view_after_ctf. Show error but let JS load challenges. if ctf_ended() and view_after_ctf(): infos.append("{} has ended".format(config.ctf_name())) return render_template( "challenges.html", infos=infos, errors=errors, start=int(start), end=int(end) )
def static_html(route): """ Route in charge of routing users to Pages. :param route: :return: """ page = get_page(route) if page is None: if (ctftime() or current_user.is_admin() or (ctf_ended() and view_after_ctf())): filename = safe_join(app.root_path, "static", route) if os.path.isfile(filename): return send_file(filename) abort(404) else: if page.auth_required and authed() is False: return redirect(url_for("auth.login", next=request.full_path)) return render_template("page.html", content=markdown(page.content))
def listing(): if (Configs.challenge_visibility == ChallengeVisibilityTypes.PUBLIC and authed() is False): pass else: if is_teams_mode() and get_current_team() is None: return redirect(url_for("teams.private", next=request.full_path)) infos = get_infos() errors = get_errors() if ctf_started() is False: errors.append(f"{Configs.ctf_name} has not started yet") if ctf_paused() is True: infos.append(f"{Configs.ctf_name} is paused") if ctf_ended() is True: infos.append(f"{Configs.ctf_name} has ended") return render_template("challenges.html", infos=infos, errors=errors)
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).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}
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() try: chal_class = get_chal_class(chal.type) except KeyError: abort( 500, f"The underlying challenge type ({chal.type}) is not installed. This challenge can not be loaded.", ) if chal.requirements: requirements = chal.requirements.get("prerequisites", []) anonymize = chal.requirements.get("anonymize") # Gather all challenge IDs so that we can determine invalid challenge prereqs all_challenge_ids = { c.id for c in Challenges.query.with_entities(Challenges.id).all() } 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 = {value for value, in solve_ids} prereqs = set(requirements).intersection(all_challenge_ids) if solve_ids >= prereqs or is_admin(): pass else: if anonymize: return { "success": True, "data": { "id": chal.id, "type": "hidden", "name": "???", "value": 0, "solves": None, "solved_by_me": False, "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 = { 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).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) solves_q, user_solves = _build_solves_query( extra_filters=(Solves.challenge_id == chal.id, )) # If there are no solves for this challenge ID then we have 0 rows maybe_row = solves_q.first() if maybe_row: challenge_id, solve_count = maybe_row solved_by_user = challenge_id in user_solves else: solve_count, solved_by_user = 0, False # Hide solve counts if we are hiding solves/accounts if scores_visible() is False or accounts_visible() is False: solve_count = None if authed(): # Get current attempts for the user attempts = Submissions.query.filter_by( account_id=user.account_id, challenge_id=challenge_id).count() else: attempts = 0 response["solves"] = solve_count response["solved_by_me"] = solved_by_user response["attempts"] = attempts response["files"] = files response["tags"] = tags response["hints"] = hints response["view"] = render_template( chal_class.templates["view"].lstrip("/"), solves=solve_count, solved_by_me=solved_by_user, files=files, tags=tags, hints=[Hints(**h) for h in hints], max_attempts=chal.max_attempts, attempts=attempts, challenge=chal, ) db.session.close() return {"success": True, "data": response}
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(ConfigTypes.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)
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 ] files = [f.location for f in chal.files] unlocked_hints = set() hints = [] if authed(): user = get_current_user() unlocked_hints = set([ u.target for u in HintUnlocks.query.filter_by( type='hints', account_id=user.account_id) ]) for hint in Hints.query.filter_by(challenge_id=chal.id).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)\ .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}