def validate_captain_id(self, data): captain_id = data.get("captain_id") if captain_id is None: return if is_admin(): team_id = data.get("id") if team_id: target_team = Teams.query.filter_by(id=team_id).first() else: target_team = get_current_team() captain = Users.query.filter_by(id=captain_id).first() if captain in target_team.members: return else: raise ValidationError("Invalid Captain ID", field_names=["captain_id"]) else: current_team = get_current_team() current_user = get_current_user() if current_team.captain_id == current_user.id: return else: raise ValidationError( "Only the captain can change team captain", field_names=["captain_id"], )
def compare(chal_key_obj, provided): saved = chal_key_obj.content data = chal_key_obj.data from fyp import generateFlag, checkShareFlag from CTFd.utils.user import get_current_team saved = generateFlag(saved, get_current_team()) if len(saved) != len(provided): return False, False result = 0 share = False if data == "case_insensitive": saved = saved.lower() provided = provided.lower() for x, y in zip(saved, provided): result |= ord(x) ^ ord(y) if result != 0: share = checkShareFlag(saved, provided) if share: from CTFd.utils.events import socketio socketio.emit( 'notification', { "title": "Share Flag Detected", "content": "Suspect team name: " + get_current_team().name }, broadcast=True) return result == 0, share
def get(self, team_id): if team_id == 'me': if not authed(): abort(403) team = get_current_team() else: if accounts_visible() is False or scores_visible() is False: abort(404) team = Teams.query.filter_by(id=team_id).first_or_404() if (team.banned or team.hidden) and is_admin() is False: abort(404) fails = team.get_fails(admin=is_admin()) view = 'admin' if is_admin() else 'user' schema = SubmissionSchema(view=view, many=True) response = schema.dump(fails) if response.errors: return {'success': False, 'errors': response.errors}, 400 if is_admin(): data = response.data else: data = [] count = len(response.data) return {'success': True, 'data': data, 'meta': {'count': count}}
def patch(self): team = get_current_team() if team.captain_id != session["id"]: return ( { "success": False, "errors": { "": ["Only team captains can edit team information"] }, }, 403, ) data = request.get_json() response = TeamSchema(view="self", instance=team, partial=True).load(data) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.commit() response = TeamSchema("self").dump(response.data) db.session.close() return {"success": True, "data": response.data}
def validate_name(self, data): name = data.get('name') if name is None: return existing_team = Teams.query.filter_by(name=name).first() # Admins should be able to patch anyone but they cannot cause a collision. if is_admin(): team_id = int(data.get('id', 0)) if team_id: if existing_team and existing_team.id != team_id: raise ValidationError('Team name has already been taken', field_names=['name']) else: # If there's no Team ID it means that the admin is creating a team with no ID. if existing_team: raise ValidationError('Team name has already been taken', field_names=['name']) else: current_team = get_current_team() # We need to allow teams to edit themselves and allow the "conflict" if data['name'] == current_team.name: return data else: name_changes = get_config('name_changes', default=True) if bool(name_changes) is False: raise ValidationError('Name changes are disabled', field_names=['name']) if existing_team: raise ValidationError('Team name has already been taken', field_names=['name'])
def request_new_challenge(challenge_id): if is_admin(): challenge = OracleChallenges.query.filter( Challenges.id == challenge_id).first_or_404() else: challenge = OracleChallenges.query.filter( OracleChallenges.id == challenge_id, and_(Challenges.state != "hidden", Challenges.state != "locked"), ).first_or_404() data = request.form or request.get_json() team_id = get_current_team().id force_new = data["force_new"] try: r = requests.post( str(challenge.oracle) + "/create", json={ "team_id": team_id, "force_new": force_new }, ) except requests.exceptions.ConnectionError: return "ERROR: Challenge oracle is not available. Talk to an admin." if r.status_code != 200: return "ERROR: Challenge oracle is not available. Talk to an admin." return r.text
def attempt(challenge, request): """ This method is used to check whether a given input is right or wrong. It does not make any changes and should return a boolean for correctness and a string to be shown to the user. It is also in charge of parsing the user's input from the request itself. :param challenge: The Challenge object from the database :param request: The request the user submitted :return: (boolean, string) """ data = request.form or request.get_json() # submission = data["submission"].strip() # instance_id = submission team_id = get_current_team().id try: r = requests.post(str(challenge.oracle) + "/attempt", json={"team_id": team_id}) except requests.exceptions.ConnectionError: return False, "Challenge oracle is not available. Talk to an admin." if r.status_code == 200: return True, "Correct" return False, "Incorrect"
def _require_complete_profile(*args, **kwargs): if authed(): if is_admin(): return f(*args, **kwargs) else: user = get_current_user() if user.filled_all_required_fields is False: info_for( "views.settings", "Please fill out all required profile fields before continuing", ) return redirect(url_for("views.settings")) if is_teams_mode(): team = get_current_team() if team and team.filled_all_required_fields is False: # This is an abort because it's difficult for us to flash information on the teams page return abort( 403, description= "Please fill in all required team profile fields", ) return f(*args, **kwargs) else: # Fallback to whatever behavior the route defaults to return f(*args, **kwargs)
def get_field_kwargs(): team = get_current_team() field_kwargs = {"editable": True} if team.filled_all_required_fields is False: # Show all fields field_kwargs = {} return field_kwargs
def get(self): # This can return None (unauth) if visibility is set to public user = get_current_user() challenges = (Challenges.query.filter( and_(Challenges.state != "hidden", Challenges.state != "locked")).order_by( Challenges.value).all()) 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()) if is_admin(): pass else: if config.is_teams_mode() and get_current_team() is None: abort(403) else: solve_ids = [] response = [] tag_schema = TagSchema(view="user", many=True) for challenge in challenges: if challenge.requirements: requirements = challenge.requirements.get("prerequisites", []) anonymize = challenge.requirements.get("anonymize") if compare(requirements, solve_ids): pass else: if anonymize: response.append({ "id": challenge.id, "type": "hidden", "name": "???", "value": 0, "category": "???", "tags": [], "template": "", "script": "", }) # Fallthrough to continue continue challenge_type = get_chal_class(challenge.type) response.append({ "id": challenge.id, "type": challenge_type.name, "name": challenge.name, "value": challenge.value, "category": challenge.category, "tags": tag_schema.dump(challenge.tags).data, "template": challenge_type.templates["view"], "script": challenge_type.scripts["view"], }) db.session.close() return {"success": True, "data": response}
def validate_captain_id(self, data): captain_id = data.get('captain_id') # if captain_id is None: # return if is_admin(): return # team_id = data.get('id') # if team_id: # target_team = Teams.query.filter_by(id=team_id).first() # else: # target_team = get_current_team() # captain = Users.query.filter_by(id=captain_id).first() # if captain in target_team.members: # return # else: # raise ValidationError('Invalid Captain ID', field_names=['captain_id']) else: if captain_id is None: return current_team = get_current_team() current_user = get_current_user() if current_team.captain_id == current_user.id: return else: raise ValidationError( 'Only the captain can change team captain', field_names=['captain_id'])
def get(self, team_id): if team_id == "me": if not authed(): abort(403) team = get_current_team() fails = team.get_fails(admin=True) else: if accounts_visible() is False or scores_visible() is False: abort(404) team = Teams.query.filter_by(id=team_id).first_or_404() if (team.banned or team.hidden) and is_admin() is False: abort(404) fails = team.get_fails(admin=is_admin()) view = "admin" if is_admin() else "user" schema = SubmissionSchema(view=view, many=True) response = schema.dump(fails) if response.errors: return {"success": False, "errors": response.errors}, 400 if is_admin(): data = response.data else: data = [] count = len(response.data) return {"success": True, "data": data, "meta": {"count": count}}
def get_current_account_name(): team = get_current_team() if team is not None: return team.name user = get_current_user() return user.name
def get(self): docker = DockerConfig.query.filter_by(id=1).first() if is_teams_mode(): session = get_current_team() tracker = DockerChallengeTracker.query.filter_by(team_id=session.id) else: session = get_current_user() tracker = DockerChallengeTracker.query.filter_by(user_id=session.id) data = list() for i in tracker: data.append({ 'id' : i.id, 'team_id' : i.team_id, 'user_id' : i.user_id, 'docker_image' : i.docker_image, 'timestamp' : i.timestamp, 'revert_time' : i.revert_time, 'instance_id' : i.instance_id, 'ports' : i.ports.split(','), 'host' : str(docker.hostname).split(':')[0] }) return { 'success' : True, 'data' : data }
def manual_get_submissions(id): # { # "data": { # "correct": [], # "pending": [ # { # "date": "2020-05-12T04:05:39.437160Z", # "provided": "WASD" # } # ] # }, # "success": true # } team = get_current_team() user = get_current_user() pending = Pending.query.filter_by( team_id=team.id if team else None, user_id=user.id, challenge_id=id, ).with_entities(Pending.date, Pending.provided).all() correct = Solves.query.filter_by( team_id=team.id if team else None, user_id=user.id, challenge_id=id, ).with_entities(Solves.date, Solves.provided).all() return jsonify({ "success": True, "data": { "correct": correct, "pending": pending } })
def tracker(): # TODO: This function shouldn't cause a DB hit for lookups if possible if authed(): track = Tracking.query.filter_by(ip=get_ip(), user_id=session['id']).first() if not track: visit = Tracking(ip=get_ip(), user_id=session['id']) db.session.add(visit) else: track.date = datetime.datetime.utcnow() try: db.session.commit() except (InvalidRequestError, IntegrityError) as e: print(e.message) db.session.rollback() session.clear() if authed(): user = get_current_user() team = get_current_team() if request.path.startswith('/themes') is False: if user and user.banned: return render_template( 'errors/403.html', error='You have been banned from this CTF'), 403 if team and team.banned: return render_template( 'errors/403.html', error='Your team has been banned from this CTF' ), 403 db.session.close()
def validate_email(self, data): email = data.get("email") if email is None: return existing_team = Teams.query.filter_by(email=email).first() if is_admin(): team_id = data.get("id") if team_id: if existing_team and existing_team.id != team_id: raise ValidationError( "Email address has already been used", field_names=["email"] ) else: if existing_team: raise ValidationError( "Email address has already been used", field_names=["email"] ) else: current_team = get_current_team() if email == current_team.email: return data else: if existing_team: raise ValidationError( "Email address has already been used", field_names=["email"] )
def validate_password_confirmation(self, data): password = data.get("password") confirm = data.get("confirm") if is_admin(): pass else: current_team = get_current_team() current_user = get_current_user() if current_team.captain_id != current_user.id: raise ValidationError( "Only the captain can change the team password", field_names=["captain_id"], ) if password and (bool(confirm) is False): raise ValidationError( "Please confirm your current password", field_names=["confirm"] ) if password and confirm: test = verify_password( plaintext=confirm, ciphertext=current_team.password ) if test is True: return data else: raise ValidationError( "Your previous password is incorrect", field_names=["confirm"] ) else: data.pop("password", None) data.pop("confirm", None)
def require_team_wrapper(*args, **kwargs): if get_config("user_mode") == TEAMS_MODE: team = get_current_team() if team is None: return redirect( url_for("teams.private", next=request.full_path)) return f(*args, **kwargs)
def get(self, attr_id, team_id): attr = Attributes.query.filter_by(id=attr_id).first_or_404() if (attr.hidden) and is_admin() is False: abort(404) intersec = IntersectionTeamAttr.query.filter_by( attr_id=attr_id).filter_by(team_id=team_id).first_or_404() if (attr.private) and is_admin() is False: t = get_current_team() if not (authed() and t and t.id == intersec.team_id): abort(404) view = IntersectionTeamAttrSchema.views.get(session.get( "type", "user")) response = IntersectionTeamAttrSchema(view=view).dump(intersec) if response.errors: return {"success": False, "errors": response.errors}, 400 if supported_input_types[attr.type] == "checkbox": if intersec.value and intersec.value != "false": response.data["value"] = True else: response.data["value"] = False return {"success": True, "data": response.data}
def get(self, team_id): if team_id == 'me': if not authed(): abort(403) team = get_current_team() else: if accounts_visible() is False or scores_visible() is False: abort(404) team = Teams.query.filter_by(id=team_id).first_or_404() awards = team.get_awards( admin=is_admin() ) schema = AwardSchema(many=True) response = schema.dump(awards) if response.errors: return { 'success': False, 'errors': response.errors }, 400 return { 'success': True, 'data': response.data }
def get(self, team_id): if team_id == 'me': if not authed(): abort(403) team = get_current_team() else: if accounts_visible() is False or scores_visible() is False: abort(404) team = Teams.query.filter_by(id=team_id).first_or_404() solves = team.get_solves( admin=is_admin() ) view = 'admin' if is_admin() else 'user' schema = SubmissionSchema(view=view, many=True) response = schema.dump(solves) if response.errors: return { 'success': False, 'errors': response.errors }, 400 return { 'success': True, 'data': response.data }
def delete(self): team_disbanding = get_config("team_disbanding", default="inactive_only") if team_disbanding == "disabled": return ( { "success": False, "errors": {"": ["Team disbanding is currently disabled"]}, }, 403, ) team = get_current_team() if team.captain_id != session["id"]: return ( { "success": False, "errors": {"": ["Only team captains can disband their team"]}, }, 403, ) # The team must not have performed any actions in the CTF performed_actions = any( [ team.solves != [], team.fails != [], team.awards != [], Submissions.query.filter_by(team_id=team.id).all() != [], Unlocks.query.filter_by(team_id=team.id).all() != [], ] ) if performed_actions: return ( { "success": False, "errors": { "": [ "You cannot disband your team as it has participated in the event. " "Please contact an admin to disband your team or remove a member." ] }, }, 403, ) for member in team.members: member.team_id = None clear_user_session(user_id=member.id) db.session.delete(team) db.session.commit() clear_team_session(team_id=team.id) clear_standings() db.session.close() return {"success": True}
def get_challenges_by_categories(): user = get_current_user() challenges = (Challenges.query.filter( and_(Challenges.state != "hidden", Challenges.state != "locked")).order_by( Challenges.value).all()) 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()) solve_ids = set([value for value, in solve_ids]) # TODO: Convert this into a re-useable decorator if is_admin(): pass else: if config.is_teams_mode() and get_current_team() is None: abort(403) else: solve_ids = set() response = [] for challenge in challenges: if challenge.requirements: requirements = challenge.requirements.get("prerequisites", []) anonymize = challenge.requirements.get("anonymize") prereqs = set(requirements) if solve_ids >= prereqs: pass else: if anonymize: response.append({ "id": challenge.id, "name": "???", "category": "???" }) # Fallthrough to continue continue response.append({ "id": challenge.id, "name": challenge.name, "category": challenge.category }) # Sort into categories categories = set(map(lambda x: x['category'], response)) cats = [] for c in categories: cats.append({ 'name': c, 'challenges': [j for j in response if j['category'] == c] }) db.session.close() return cats
def get(self): team = get_current_team() response = TeamSchema(view='self').dump(team) if response.errors: return {'success': False, 'errors': response.errors}, 400 return {'success': True, 'data': response.data}
def get(self): team = get_current_team() response = TeamSchema(view="self").dump(team) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data}
def validate_email(self, data): email = data.get('email') if email is None: return obj = Teams.query.filter_by(email=email).first() if obj: if is_admin(): if data.get('id'): target_user = Teams.query.filter_by(id=data['id']).first() else: target_user = get_current_team() if target_user and obj.id != target_user.id: raise ValidationError('Email address has already been used', field_names=['email']) else: if obj.id != get_current_team().id: raise ValidationError('Email address has already been used', field_names=['email'])
def require_team_wrapper(*args, **kwargs): if get_config("user_mode") == TEAMS_MODE: team = get_current_team() if team is None: if request.content_type == "application/json": abort(403) else: return redirect(url_for("teams.private", next=request.full_path)) return f(*args, **kwargs)
def get(self): team = get_current_team() awards = team.get_awards(admin=True) schema = AwardSchema(many=True) response = schema.dump(awards) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data}
def get(self): container = request.args.get('name') if not container: return abort(403) docker = DockerConfig.query.filter_by(id=1).first() containers = DockerChallengeTracker.query.all() if container not in get_repositories(docker, tags=True): return abort(403) if is_teams_mode(): session = get_current_team() # First we'll delete all old docker containers (+2 hours) for i in containers: if int(session.id) == int(i.team_id) and (unix_time(datetime.utcnow()) - int(i.timestamp)) >= 7200: delete_container(docker, i.instance_id) DockerChallengeTracker.query.filter_by(instance_id=i.instance_id).delete() db.session.commit() check = DockerChallengeTracker.query.filter_by(team_id=session.id).filter_by(docker_image=container).first() else: session = get_current_user() for i in containers: if int(session.id) == int(i.user_id) and (unix_time(datetime.utcnow()) - int(i.timestamp)) >= 7200: delete_container(docker, i.instance_id) DockerChallengeTracker.query.filter_by(instance_id=i.instance_id).delete() db.session.commit() check = DockerChallengeTracker.query.filter_by(user_id=session.id).filter_by(docker_image=container).first() # If this container is already created, we don't need another one. if check != None and not (unix_time(datetime.utcnow()) - int(check.timestamp)) >= 300: return abort(403) # The exception would be if we are reverting a box. So we'll delete it if it exists and has been around for more than 5 minutes. elif check != None: delete_container(docker, check.instance_id) if is_teams_mode(): DockerChallengeTracker.query.filter_by(team_id=session.id).filter_by(docker_image=container).delete() else: DockerChallengeTracker.query.filter_by(user_id=session.id).filter_by(docker_image=container).delete() db.session.commit() portsbl = get_unavailable_ports(docker) create = create_container(docker,container,session.name,portsbl) print(create) ports = json.loads(create[1])['HostConfig']['PortBindings'].values() entry = DockerChallengeTracker( team_id = session.id if is_teams_mode() else None, user_id = session.id if not is_teams_mode() else None, docker_image = container, timestamp = unix_time(datetime.utcnow()), revert_time = unix_time(datetime.utcnow()) + 300, instance_id = create[0]['Id'], ports = ','.join([p[0]['HostPort'] for p in ports]), host = str(docker.hostname).split(':')[0] ) db.session.add(entry) db.session.commit() db.session.close() return