def team_invite_rescind(): params = utils.flat_multi(request.form) _user = user.get_user().first() _team = get_team(tid=_user.tid).first() if _user.uid != _team.owner: raise WebException( "You must be the captain of your team to rescind invitations!") if _team.finalized: raise WebException("This team is finalized.") uid = params.get("uid") if uid is None: raise WebException("Please provide a user.") invitation = TeamInvitations.query.filter_by(rtype=0, frid=_team.tid, toid=uid).first() if invitation is None: raise WebException("Invitation doesn't exist.") with app.app_context(): db.session.delete(invitation) db.session.commit() db.session.close() return {"success": 1, "message": "Success!"}
def team_remove_member(): username = session.get("username") tid = session.get("tid") team = Teams.query.filter_by(tid=tid).first() usr = Users.query.filter_by(username=username).first() owner = team.owner if team.finalized: raise WebException("This team is finalized.") if usr.uid == owner or usr.admin: params = utils.flat_multi(request.form) to_remove = params.get("username") if to_remove is None: return {"success": 0, "message": "ur high"} user_to_remove = Users.query.filter_by( username_lower=to_remove.lower()).first() if user_to_remove is None: return {"success": 0, "message": "User not found."} if user_to_remove.tid != tid: return {"success": 0, "message": "This user is not on your team!"} with app.app_context(): Users.query.filter_by(uid=user_to_remove.uid).update({"tid": -1}) db.session.commit() db.session.close() return {"success": 1, "message": "Success!"} else: raise WebException("Not authorized.")
def user_avatar_upload(): logged_in = is_logged_in() if not logged_in: raise WebException("You're not logged in.") _user = get_user().first() f = request.files["file"] if f is None: raise WebException("Please upload something.") fname = "/tmp/" + secure_filename(utils.generate_string()) f.save(fname) try: pfp = os.path.join(app.config["PFP_FOLDER"], "%d.png" % _user.uid) if os.path.exists(pfp): os.remove(pfp) with open(fname, "rb") as f1: imageIO = io.BytesIO(f1.read()) imageIO.seek(0) f1.close() im = Image.open(imageIO) im = im.resize((256, 256), Image.ANTIALIAS) im.save(open(pfp, "w"), "PNG") return {"success": 1, "message": "Uploaded!"} except Exception, e: raise WebException(str(e))
def user_update_profile(): params = utils.flat_multi(request.form) password = params.get("current_password") new_password = params.get("new_password") new_password_confirm = params.get("new_password_confirm") email = params.get("email") if new_password != new_password_confirm: raise WebException("Passwords do not match.") user = get_user(username=session["username"]).first() correct = utils.check_password(user.password, password) if not correct: raise WebException("Incorrect password.") if new_password != "": user.password = utils.hash_password(new_password) if email != user.email: if get_user(email=email.lower()).count() > 0: raise WebException("This email is taken!") user.email = email.lower() user.email_verified = False current_session = db.session.object_session(user) current_session.add(user) current_session.commit() return {"success": 1, "message": "Profile updated."}
def clone_repository(payload): GIT_REPO = str(os.path.join(GIT_DIR, payload["delivery_id"])) if os.path.exists(GIT_REPO): shutil.rmtree(GIT_REPO) repo = git.Repo.init(GIT_REPO) origin = repo.create_remote("origin", payload["repository"]["ssh_url"]) with open(KEYFILE, "w") as f: f.write(utils.get_ssh_keys()[0]) with open(SSH_CONFIG_FILE, "w") as f: f.write("Host *\n\tStrictHostKeyChecking no") os.chmod(KEYFILE, 0600) os.chmod(SSH_CONFIG_FILE, 0600) if os.system("cd %s; ssh-agent bash -c 'ssh-add %s; git pull origin master'" % (GIT_REPO, KEYFILE)) == 0: os.unlink(KEYFILE) problems = [] for problem in os.listdir(GIT_REPO): problem = str(problem) if problem in [".", "..", ".git", ".exclude"]: continue if not os.path.isdir(os.path.join(GIT_REPO, problem)): continue files = os.listdir(os.path.join(GIT_REPO, problem)) for required_file in ["grader.py", "problem.yml", "description.md"]: if required_file not in files: raise WebException("Expected required file %s in '%s'." % (required_file, problem)) metadata = yaml.load(open(os.path.join(GIT_REPO, problem, "problem.yml"))) for required_key in ["title", "category", "value"]: if required_key not in metadata: raise WebException("Expected required key %s in 'problem.yml'." % required_key) problems.append(problem) # thread = threading.Thread(target=import_repository, args=(GIT_REPO, problems)) import_repository(GIT_REPO, problems) else: raise WebException("Failed to pull from remote.")
def user_twofactor_verify(): _user = get_user().first() if _user is None: raise WebException("User not found.") params = utils.flat_multi(request.form) pwd = params.get("password") if pwd is None: raise WebException("Please enter your password.") if not utils.check_password(_user.password, pwd): raise WebException("Incorrect password.") if "token" not in params: raise WebException("Invalid token.") token = params.get("token") if not (_user.verify_totp(int(token))): raise WebException("Invalid token. Current server time: " + time.strftime("%Y-%m-%d %H:%M:%S")) with app.app_context(): Users.query.filter_by(uid=_user.uid).update({"otp_confirmed": True}) db.session.commit() print "CONFIRMED" return {"success": 1, "message": "Confirmed!"}
def team_accept_invite(): params = utils.flat_multi(request.form) _user = user.get_user().first() if user.in_team(_user): raise WebException("You're already in a team!") tid = params.get("tid") _team = get_team(tid=tid).first() if _team is None: raise WebException("Team not found.") if _team.finalized: raise WebException("This team is finalized.") if len(_team.get_members()) >= utils.get_config("team_size"): raise WebException("This team is full.") invitation = TeamInvitations.query.filter_by(rtype=0, frid=tid, toid=_user.uid).first() if invitation is None: raise WebException("Invitation doesn't exist.") with app.app_context(): _user = Users.query.filter_by(uid=_user.uid).first() _user.tid = tid db.session.delete(invitation) invitation2 = TeamInvitations.query.filter_by(rtype=1, frid=_user.uid, toid=tid).first() if invitation2 is not None: db.session.delete(invitation2) db.session.commit() db.session.close() return {"success": 1, "message": "Success!"}
def problem_submit(): params = utils.flat_multi(request.form) pid = params.get("pid") flag = params.get("flag") tid = session.get("tid") _user = user.get_user().first() username = _user.username problem = Problems.query.filter_by(pid=pid).first() team = Teams.query.filter_by(tid=tid).first() solved = Solves.query.filter_by(pid=pid, tid=tid, correct=1).first() if solved: raise WebException("You already solved this problem.") flag_tried = Solves.query.filter_by(pid=pid, flag=flag).first() if flag_tried: raise WebException("Your team has already tried this solution.") if problem: if problem.category == "Programming": raise WebException( "Please submit programming problems using the Programming interface." ) grader = imp.load_source("grader", problem.grader) random = None if problem.autogen: random = autogen.get_random(pid, tid) correct, response = grader.grade(random, flag) solve = Solves(pid, _user.uid, tid, flag, correct) db.session.add(solve) db.session.commit() if correct: # Wait until after the solve has been added to the database before adding bonus solves = get_solves(pid) solve.bonus = [-1, solves][solves < 4] cache.invalidate_memoization(get_solves, pid) if _user: activity = UserActivity(_user.uid, 3, tid=tid, pid=pid) db.session.add(activity) db.session.commit() logger.log(__name__, "%s has solved %s by submitting %s" % (team.teamname, problem.title, flag), level=logger.WARNING) return {"success": 1, "message": response} else: logger.log(__name__, "%s has incorrectly submitted %s to %s" % (team.teamname, flag, problem.title), level=logger.WARNING) raise WebException(response) else: raise WebException("Problem does not exist!")
def activity_user(): params = utils.flat_multi(request.args) if "user" not in params: raise WebException("Please specify a user.") _user = get_user(username_lower=params.get("user").lower()).first() if _user is None: raise WebException("User not found.") return _user.get_activity()
def team_finalize(): _user = user.get_user().first() _team = get_team(tid=_user.tid).first() if _user.uid != _team.owner: raise WebException( "You must be the captain of your team to finalize the team!") if _team.finalized: raise WebException("This team is already finalized.") _team.finalize() return {"success": 1}
def user_avatar_remove(): logged_in = is_logged_in() if not logged_in: raise WebException("You're not logged in.") _user = get_user().first() try: pfp = os.path.join(app.config["PFP_FOLDER"], "%d.png" % _user.uid) os.remove(pfp) return {"success": 1, "message": "Removed!"} except Exception, e: raise WebException(str(e))
def send_verification(username, email, token): verification_link = "%s/settings/verify?token=%s" % ("127.0.0.1:8080", token) subject = utils.get_ctf_name() + " Email Verification" body = """Hi %s!\n\nHelp us secure your %s account by verifying your email below:\n\n%s\n\nIf believe this is a mistake, you may safely ignore this email and delete it.\n\nGood luck!\n\n- OpenCTF Administrator""" % ( username, utils.get_config("ctf_name"), verification_link) response = utils.send_email(email, subject, body) if response.status_code != 200: raise WebException("Could not send email.") response = response.json() if "Queued" in response["message"]: return True else: raise WebException(response["message"])
def problem_data(): if "admin" in session and session["admin"]: pass elif "tid" not in session or session["tid"] <= 0: raise WebException("You need a team.") elif team.get_team(tid=session.get("tid")).first().finalized != True: raise WebException("Your team is not finalized.") problems = Problems.query.order_by(Problems.value).all() problems_return = [] for problem in problems: solves = get_solves(problem.pid) solved = Solves.query.filter_by(pid=problem.pid, tid=session.get("tid", None), correct=1).first() solved = ["Solved", "Unsolved"][solved is None] description = process_description(problem.description) data = { "pid": problem.pid, "title": problem.title, "category": problem.category, "description": description, "hint": problem.hint, "value": problem.value, "solves": solves, "solved": solved } admin_data = { "description_source": problem.description, "threshold": problem.threshold, "weightmap": problem.weightmap, "grader_contents": open(problem.grader, "r").read(), "bonus": problem.bonus, "autogen": problem.autogen == True } if "admin" in session and session["admin"]: data.update(admin_data) if problem.autogen: grader = imp.load_source("grader", problem.grader) tid = session.get("tid", "team") try: data.update( grader.generate_problem( autogen.get_random(problem.pid, tid), problem.pid)) except Exception, e: logger.log( __name__, "The grader for \"%s\" has thrown an error: %s" % (problem.title, e)) problems_return.append(data)
def user_forgot_password(token=None): params = utils.flat_multi(request.form) if token is not None: user = get_user(reset_token=token).first() if user is None: raise WebException("Invalid reset token.") # We are viewing the actual reset form if request.method == "GET": return {"success": 1, "message": ""} # Submission of actual reset form if request.method == "POST": password = params.get("password") confirm_password = params.get("confirm_password") if password != confirm_password: raise WebException("Passwords do not match.") else: user.password = utils.hash_password(password) user.reset_token = None current_session = db.session.object_session(user) current_session.add(user) current_session.commit() return {"success": 1, "message": "Success!"} else: email = params.get("email").lower() user = get_user(email=email).first() if user is None: raise WebException("User with that email does not exist.") token = utils.generate_string(length=64) user.reset_token = token current_session = db.session.object_session(user) current_session.add(user) current_session.commit() reset_link = "%s/forgot/%s" % ("127.0.0.1:8000", token) subject = "OpenCTF password reset" body = """%s,\n\nA request to reset your OpenCTF password has been made. If you did not request this password reset, you may safely ignore this email and delete it.\n\nYou may reset your password by clicking this link or pasting it to your browser.\n\n%s\n\nThis link can only be used once, and will lead you to a page where you can reset your password.\n\nGood luck!\n\n- OpenCTF Administrator""" % ( user.username, reset_link) response = utils.send_email(email, subject, body) if response.status_code != 200: raise WebException("Could not send email") response = response.json() if "Queued" in response["message"]: return {"success": 1, "message": "Email sent to %s" % email} else: raise WebException(response["message"])
def login(): form = request.form email = form.get("email") password = form.get("password") user = get_user(email=email).first() if user is None: raise WebException("Invalid credentials.") if utils.check_hash(user.password, password): session["uid"] = user.uid session["logged_in"] = True return { "success": 1, "message": "Success!" } raise WebException("Invalid credentials.")
def team_delete(): params = utils.flat_multi(request.form) if "tid" in params: tid = params.get("tid") else: tid = session.get("tid") username = session["username"] team = Teams.query.filter_by(tid=tid).first() usr = Users.query.filter_by(username=username).first() owner = team.owner if usr.uid == owner or usr.admin: with app.app_context(): for member in Users.query.filter_by(tid=tid).all(): member.tid = -1 db.session.add(member) UserActivity.query.filter_by(tid=tid).delete() Solves.query.filter_by(tid=tid).delete() ProgrammingSubmissions.query.filter_by(tid=tid).delete() db.session.delete(team) db.session.commit() db.session.close() session.pop("tid") return {"success": 1, "message": "Success!"} else: raise WebException("Not authorized.")
def user_register(): params = utils.flat_multi(request.form) if params.get("password") != params.get("password_confirm"): raise WebException("Passwords do not match.") verify_to_schema(UserSchema, params) name = params.get("name") email = params.get("email") username = params.get("username") password = params.get("password") password_confirm = params.get("password_confirm") utype = int(params.get("type")) register_user(name, username, email, password, utype, admin=False) login_user(username, password) logger.log( __name__, "%s registered with %s" % (name.encode("utf-8"), email.encode("utf-8"))) try: send_verification(username, email, token) except: return { "success": 1, "message": "Verification email sent to %s" % email } return {"success": 0, "message": "Failed."}
def user_session_delete(): params = utils.flat_multi(request.form) sid = params.get("sid") if sid is None: raise WebException("Please specify which session to delete.") with app.app_context(): _session = LoginTokens.query.filter_by(sid=sid).first() _user = get_user().first() if _session.uid != _user.uid: raise WebException("That's not your token!") LoginTokens.query.filter_by(sid=sid).update({"active": False}) db.session.commit() assert (LoginTokens.query.filter_by(sid=sid).first().active == False) return {"success": 1, "message": "Deleted."}
def judge(submission_path, language, pid): if not os.path.exists(submission_path): raise WebException("Program is missing.") _problem = problem.get_problem(pid=pid).first() if _problem is None: raise WebException("Problem does not exist.") submission_root = os.path.dirname(submission_path) os.chdir(submission_root) log = "" message = "" log += "Compiling...\n" start_time = time.time() try: if language == "python2": subprocess.check_output("python -m py_compile %s" % submission_path, shell=True) elif language == "python3": subprocess.check_output("python3 -m py_compile %s" % submission_path, shell=True) elif language == "java": subprocess.check_output("javac %s" % submission_path, shell=True) else: message = "Not implemented." return message, log, time.time() - start_time except subprocess.CalledProcessError as e: # TODO: Extract useful error messages from exceptions and add timeout #log += "There was a problem with compiling.\n%s\n" % str(e) message = "There was a problem with compiling." return message, log, time.time() - start_time log += "Compiled.\n" try: judge = imp.load_source("judge", _problem.grader) except Exception, e: message = "An error occured. Please notify an admin immediately." log += "Could not load judge.\n" return message, log, time.time() - start_time
def validate_grader(grader_contents, autogen=False): tmp_grader = "/tmp/grader.py" open(tmp_grader, "w").write(grader_contents) try: grader = imp.load_source("grader", tmp_grader) except Exception, e: raise WebException("There is a syntax error in the grader: %s" % e)
def validate_judge(judge_contents): tmp_judge = "/tmp/judge.py" open(tmp_judge, "w").write(judge_contents) try: judge = imp.load_source("judge", tmp_judge) except Exception, e: raise WebException("There is a syntax error in the judge: %s" % e)
def get_problems(): if session.get("admin"): pass elif session.get("tid") <= 0: raise WebException("You need a team.") elif team.get_team(tid=session.get("tid")).first().finalized != True: raise WebException("Your team is not finalized.") data = [] problems = Problems.query.filter_by(category="Programming").all() if problems is not None: for _problem in problems: data.append({ "title": _problem.title, "pid": _problem.pid, "value": _problem.value }) return {"success": 1, "problems": data}
def get_team_info(tid=None, teamname=None, teamname_lower=None, owner=None): team = get_team(tid=tid, teamname=teamname, teamname_lower=teamname_lower, owner=owner).first() if team is None: raise WebException("Team not found.") result = team.get_info() return result
def v(value): for callbacks, msg in callback_tuples: for callback in callbacks: try: result = callback(value) if not result and type(result) == bool: raise Invalid(msg) except Exception: raise WebException(msg) return value
def remove_task(): form = request.form tid = form.get("tid") result = get_task(tid=tid) task = result.first() if task is None: raise WebException("Task does not exist.") result.delete() db.session.commit() return {"success": 1, "message": "Task deleted."}
def verify_email(): # Actual verification of email if request.method == "GET": params = utils.flat_multi(request.args) token = params.get("token") if token is not None: user = Users.query.filter_by(email_token=token).first() if user is None: raise WebException("Invalid token.") user.email_verified = True user.email_token = None current_session = db.session.object_session(user) current_session.add(user) current_session.commit() return {"success": 1, "message": "Email verified."} raise WebException("Invalid token.") # Request to verify email elif request.method == "POST": user = get_user().first() if user is None: raise WebException("User with that username does not exist.") if user.email_verified: raise WebException("Email is already verified.") token = utils.generate_string(length=64) user.email_token = token current_session = db.session.object_session(user) current_session.add(user) current_session.commit() try: send_verification(user.username, user.email, token) except: return {"success": 0, "message": "Failed."} return { "success": 1, "message": "Verification email sent to %s" % user.email }
def remove_project(): form = request.form pid = form.get("pid") result = get_project(pid=pid) project = result.first() if project is None: raise WebException("Project does not exist.") Task.query.filter_by(project=pid).delete() result.delete() db.session.commit() return {"success": 1, "message": "Project deleted."}
def add_problem(title, category, description, value, grader_contents, pid=utils.generate_string(), hint="", bonus=0, autogen=0): grader_contents = str(grader_contents) pid = str(pid) value = int(value) title_exists = Problems.query.filter_by(title=title).first() if title_exists: raise WebException("Problem name already taken.") while Problems.query.filter_by(pid=pid).first(): pid = utils.generate_string() if category == "Programming": programming.validate_judge(grader_contents) else: validate_grader(grader_contents, autogen=int(autogen)) grader_folder = os.path.join(app.config["GRADER_FOLDER"], pid) if not os.path.exists(grader_folder): os.makedirs(grader_folder) grader_path = os.path.join(grader_folder, "grader.py") grader_file = open(grader_path, "w") grader_file.write(grader_contents) grader_file.close() problem = Problems(pid, title, category, description, value, grader_path, bonus=bonus, hint=hint, autogen=autogen) db.session.add(problem) files = request.files.getlist("files[]") for _file in files: filename = secure_filename(_file.filename) if len(filename) == 0: continue file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename) _file.save(file_path) db_file = Files(problem.pid, "/".join(file_path.split("/")[2:])) db.session.add(db_file) db.session.commit() db.session.close()
def register(): form = request.form name = form.get("name") email = form.get("email") password = form.get("password") confirm_password = form.get("confirm_password") if password != confirm_password: raise WebException("Passwords do not match.") if len(password) < 4: raise WebException("Passwords should be at least four characters long.") user = get_user(email=email).first() if user is not None: raise WebException("Email already in use.") user = User(name, email, password) db.session.add(user) db.session.commit() return { "success": 1, "message": "Success!" }
def admin_setup_init(): if utils.is_setup_complete(): raise WebException("Setup has already been complete.") verification = Config("setup_verification", utils.generate_string().lower()) with app.app_context(): for item in Config.query.filter_by(key="setup_verification").all(): db.session.delete(item) db.session.add(verification) db.session.commit() db.session.close() return { "success": 1 }