Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def run_instance():
challenge_name = request.form.get("challenge_name", None)

# Disable captcha on debug mode
if not current_app.config["ENABLE_RECAPTCHA"] and not recaptcha.verify():
if not current_app.config["DEBUG"] and current_app.config["ENABLE_RECAPTCHA"] and not recaptcha.verify():
flash("Captcha failed.", "red")
return redirect(url_for('index'))

Expand All @@ -222,8 +222,8 @@ def run_instance():
flash("The challenge name is not valid.", "red")
return redirect(url_for('index'))

if get_challenge_count_per_team(session["team_id"]) >= MAX_CHALLENGES_PER_TEAM:
flash(f"Your team has reached the maximum number of concurrent running instances ({MAX_CHALLENGES_PER_TEAM}).", "red")
if get_challenge_count_per_team(session["team_id"]) >= MAX_INSTANCE_PER_TEAM:
flash(f"Your team has reached the maximum number of concurrent running instances ({MAX_INSTANCE_PER_TEAM}).", "red")
return redirect(url_for('index'))

remove_user_running_instance(session["user_id"])
Expand Down
2 changes: 1 addition & 1 deletion app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def create_app():
app.config["SQLALCHEMY_DATABASE_URI"] = getenv("DATABASE_URI")
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

app.config["ENABLE_RECAPTCHA"] = getenv("ENABLE_RECAPTCHA", False)
app.config["ENABLE_RECAPTCHA"] = getenv("ENABLE_RECAPTCHA", False) in ["1", "True", "TRUE"]
app.config["RECAPTCHA_SITE_KEY"] = getenv("RECAPTCHA_SITE_KEY", "")
app.config["RECAPTCHA_SECRET_KEY"] = getenv("RECAPTCHA_SECRET_KEY", "")

Expand Down
47 changes: 39 additions & 8 deletions app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,25 @@ def check_challenge_name(challenge_name):
return True
return False

def check_mode(key):
"""
Returns the mode of the CTFd instance (teams or user)
"""
base_url = CTFD_URL.strip('/')
try:
req = requests.get(f"{base_url}/api/v1/teams",
headers={"Authorization":f"Token {key}", "Content-Type": "application/json"})
# If /api/v1/teams endpoint is accessible, then CTF mode is teams, otherwise if 404 it's user. Dirty but works.
if req.status_code == 200:
return "teams"
elif req.status_code == 404:
return "user"
else:
return False
except Exception as err:
current_app.logger.error(f"Error checking mode: {err}")
current_app.logger.error("Error: %s", err)
return False

def check_access_key(key):
"""
Expand All @@ -202,7 +221,7 @@ def check_access_key(key):
"team_name": None,
"is_admin": False
}

pattern = r'^ctfd_[a-zA-Z0-9]+$'
if not re.match(pattern, key):
return False, "Invalid access key, wrong format!", user
Expand All @@ -215,18 +234,30 @@ def check_access_key(key):
success = resp_json.get("success", False)
user["user_id"] = resp_json.get("data", {}).get("id", "")
user["username"] = resp_json.get("data", {}).get("name", "")
user["team_id"] = resp_json.get("data", {}).get("team_id", False)

# User is not in a team
if not success or not user["team_id"]:
return False, "User not in a team or invalid token.", user
mode = check_mode(key)
current_app.logger.error(mode)

if mode == "teams":
user["team_id"] = resp_json.get("data", {}).get("team_id", False)

resp_json = requests.get(f"{base_url}/api/v1/teams/{user['team_id']}",
headers={"Authorization":f"Token {key}", "Content-Type":"application/json"}).json()
user["team_name"] = resp_json.get("data", {}).get("name", "")
# User is not in a team
if not success or not user["team_id"]:
return False, "User not in a team or invalid token.", user

resp_json = requests.get(f"{base_url}/api/v1/teams/{user['team_id']}",
headers={"Authorization":f"Token {key}", "Content-Type":"application/json"}).json()
user["team_name"] = resp_json.get("data", {}).get("name", "")
# Easy workaround to keep the app working even in solo mode is to emulate a fake team based on user id and username :)
elif mode == "user":
user["team_id"] = resp_json.get("data", {}).get("id", "")
user["team_name"] = resp_json.get("data", {}).get("name", "")
else:
return False, "An error occurred while checking the CTF mode"

resp_json = requests.get(f"{base_url}/api/v1/configs", headers={"Authorization":f"Token {key}", "Content-Type":"application/json"}).json()
user["is_admin"] = resp_json.get("success", False)

return True, "", user
except Exception as err:
current_app.logger.error("Unable to reach CTFd with access key: %s", key)
Expand Down