def create(request): """ This method is used to process the challenge creation request. :param request: :return: """ # Create challenge chal = Challenges( name=request.form['name'], description=request.form['description'], value=request.form['value'], category=request.form['category'], type=request.form['chaltype'], penalty = request.form['penalty'] ) if 'hidden' in request.form: chal.hidden = True else: chal.hidden = False max_attempts = request.form.get('max_attempts') if max_attempts and max_attempts.isdigit(): chal.max_attempts = int(max_attempts) db.session.add(chal) db.session.commit() flag = Keys(chal.id, request.form['key'], request.form['key_type[0]']) if request.form.get('keydata'): flag.data = request.form.get('keydata') db.session.add(flag) db.session.commit() files = request.files.getlist('files[]') for f in files: utils.upload_file(file=f, chalid=chal.id) file_generators = request.files.getlist('file_generators[]') for g in file_generators: utils.upload_file(file=g, chalid=chal.id, isgenerator=True) db.session.commit()
def gen_challenge(db, name='chal_name', description='chal_description', value=100, category='chal_category', type='standard', state='visible', **kwargs): chal = Challenges(name=name, description=description, value=value, category=category, type=type, state=state, **kwargs) db.session.add(chal) db.session.commit() return chal
def gen_challenge(db, name="chal_name", description="chal_description", value=100, category="chal_category", type="standard", state="visible", **kwargs): chal = Challenges(name=name, description=description, value=value, category=category, type=type, state=state, **kwargs) db.session.add(chal) db.session.commit() return chal
def admin_create_chal(): files = request.files.getlist('files[]') ## TODO: Expand to support multiple flags flags = [{ 'flag': request.form['key'], 'type': int(request.form['key_type[0]']) }] # Create challenge chal = Challenges(request.form['name'], request.form['desc'], request.form['value'], request.form['category'], flags) if 'hidden' in request.form: chal.hidden = True else: chal.hidden = False db.session.add(chal) db.session.commit() for f in files: filename = secure_filename(f.filename) if len(filename) <= 0: continue md5hash = hashlib.md5(os.urandom(64)).hexdigest() if not os.path.exists( os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash)): os.makedirs( os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash)) f.save( os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash, filename)) db_f = Files(chal.id, os.path.join('static', 'uploads', md5hash, filename)) db.session.add(db_f) db.session.commit() db.session.close() return redirect(url_for('admin.admin_chals'))
def admin_create_chal(): files = request.files.getlist('files[]') # Create challenge chal = Challenges(request.form['name'], request.form['desc'], request.form['value'], request.form['category']) db.session.add(chal) db.session.commit() # Add keys key = Keys(chal.id, request.form['key'], request.form['key_type[0]']) db.session.add(key) db.session.commit() for f in files: filename = secure_filename(f.filename) if len(filename) <= 0: continue md5hash = hashlib.md5(filename).hexdigest() if not os.path.exists( os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash)): os.makedirs( os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash)) f.save( os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash, filename)) db_f = Files( chal.id, os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash, filename)) db.session.add(db_f) db.session.commit() db.session.close() return redirect('/admin/chals')
def admin_create_chal(): if request.method == 'POST': files = request.files.getlist('files[]') # Create challenge chal = Challenges( name=request.form['name'], description=request.form['desc'], value=request.form['value'], category=request.form['category'], type=request.form['chaltype'] ) if 'hidden' in request.form: chal.hidden = True else: chal.hidden = False max_attempts = request.form.get('max_attempts') if max_attempts and max_attempts.isdigit(): chal.max_attempts = int(max_attempts) db.session.add(chal) db.session.flush() flag = Keys(chal.id, request.form['key'], int(request.form['key_type[0]'])) if request.form.get('keydata'): flag.data = request.form.get('keydata') db.session.add(flag) db.session.commit() for f in files: utils.upload_file(file=f, chalid=chal.id) db.session.commit() db.session.close() return redirect(url_for('admin_challenges.admin_chals')) else: return render_template('admin/chals/create.html')
def import_challenges(in_file, dst_attachments, exit_on_error=True, move=False): from CTFd.models import db, Challenges, Flags, Tags, ChallengeFiles from CTFd.utils import uploads chals = [] with open(in_file, 'r') as in_stream: chals = yaml.safe_load_all(in_stream) for chal in chals: skip = False for req_field in REQ_FIELDS: if req_field not in chal: if exit_on_error: raise MissingFieldError(req_field) else: print "Skipping challenge: Missing field '{}'".format( req_field) skip = True break if skip: continue for flag in chal['flags']: if 'flag' not in flag: if exit_on_error: raise MissingFieldError('flag') else: print "Skipping flag: Missing field 'flag'" continue flag['flag'] = flag['flag'].strip() if 'type' not in flag: flag['type'] = "static" matching_chal = Challenges.query.filter_by( name=chal['name'].strip()).first() if matching_chal: print "Updating {}: Duplicate challenge found in DB (id: {})".format( chal['name'].encode('utf8'), matching_chal.id) Tags.query.filter_by(challenge_id=matching_chal.id).delete() ChallengeFiles.query.filter_by( challenge_id=matching_chal.id).delete() Flags.query.filter_by(challenge_id=matching_chal.id).delete() #Hints.query.filter_by(challenge_id=matching_chal.id).delete() matching_chal.name = chal['name'].strip() matching_chal.description = chal['description'].strip() matching_chal.category = chal['category'].strip() if chal.get('type', 'standard') == 'standard': matching_chal.value = chal['value'] db.session.commit() chal_dbobj = matching_chal else: print "Adding {}".format(chal['name'].encode('utf8')) chal_type = chal.get('type', 'standard') if chal_type == 'standard': # We ignore traling and leading whitespace when importing challenges chal_dbobj = Challenges( name=chal['name'].strip(), description=chal['description'].strip(), value=chal['value'], category=chal['category'].strip(), ) elif chal_type == 'dynamic': from CTFd.plugins.dynamic_challenges import DynamicChallenge # We ignore traling and leading whitespace when importing challenges chal_dbobj = DynamicChallenge( name=chal['name'].strip(), description=chal['description'].strip(), category=chal['category'].strip(), value=int(chal['value']), minimum=int(chal['minimum']), decay=int(chal['decay']), ) else: raise ValueError('Unknown type of challenge') db.session.add(chal_dbobj) db.session.commit() for tag in chal.get('tags', []): tag_dbobj = Tags(challenge_id=chal_dbobj.id, value=tag) db.session.add(tag_dbobj) for flag in chal['flags']: flag_db = Flags(challenge_id=chal_dbobj.id, content=flag['flag'], type=flag['type']) db.session.add(flag_db) prerequisites = set() for prerequisite in chal.get('prerequisites', []): prerequisites.update([ c.id for c in Challenges.query.filter_by( name=prerequisite).all() ]) chal_dbobj.requirements = {'prerequisites': list(prerequisites)} if 'files' in chal: from io import FileIO for filename in chal['files']: # upload_file takes a werkzeug.FileStorage object, but we can get close enough # with a file object with a filename property added filepath = os.path.join(os.path.dirname(in_file), filename) with FileIO(filepath, mode='rb') as f: f.filename = os.path.basename(f.name) uploads.upload_file(file=f, challenge=chal_dbobj.id, type='challenge') db.session.commit() db.session.close()
def random_chance(): return random.random() > 0.5 if __name__ == "__main__": with app.app_context(): db = app.db # Generating Challenges print("GENERATING CHALLENGES") for x in range(CHAL_AMOUNT): word = gen_word() chal = Challenges( name=word, description=gen_sentence(), value=gen_value(), category=gen_category(), ) db.session.add(chal) db.session.commit() f = Flags(challenge_id=x + 1, content=word, type="static") db.session.add(f) db.session.commit() # Generating Files print("GENERATING FILES") AMT_CHALS_WITH_FILES = int(CHAL_AMOUNT * (3.0 / 4.0)) for x in range(AMT_CHALS_WITH_FILES): chal = random.randint(1, CHAL_AMOUNT) filename = gen_file() md5hash = hashlib.md5(filename.encode("utf-8")).hexdigest()
def random_date(start, end): return start + datetime.timedelta( seconds=random.randint(0, int((end - start).total_seconds()))) if __name__ == '__main__': with app.app_context(): db = app.db # Generating Challenges print("GENERATING CHALLENGES") for x in range(CHAL_AMOUNT): word = gen_word() db.session.add( Challenges(word, gen_sentence(), gen_value(), gen_category())) db.session.commit() db.session.add(Keys(x + 1, word, 0)) db.session.commit() # Generating Files print("GENERATING FILES") AMT_CHALS_WITH_FILES = int(CHAL_AMOUNT * (3.0 / 4.0)) for x in range(AMT_CHALS_WITH_FILES): chal = random.randint(1, CHAL_AMOUNT) filename = gen_file() md5hash = hashlib.md5(filename.encode('utf-8')).hexdigest() db.session.add(Files(chal, md5hash + '/' + filename)) db.session.commit()
def import_challenges(in_file, dst_attachments, exit_on_error=True, move=False): from CTFd.models import db, Challenges, Keys, Tags, Files chals = [] with open(in_file, 'r') as in_stream: chals = yaml.safe_load_all(in_stream) for chal in chals: skip = False for req_field in REQ_FIELDS: if req_field not in chal: if exit_on_error: raise MissingFieldError(req_field) else: print "Skipping challenge: Missing field '{}'".format(req_field) skip = True break if skip: continue for flag in chal['flags']: if 'flag' not in flag: if exit_on_error: raise MissingFieldError('flag') else: print "Skipping flag: Missing field 'flag'" continue flag['flag'] = flag['flag'].strip() if 'type' not in flag: flag['type'] = "static" # We ignore traling and leading whitespace when importing challenges chal_dbobj = Challenges( chal['name'].strip(), chal['description'].strip(), chal['value'], chal['category'].strip() ) if 'hidden' in chal and chal['hidden']: chal_dbobj.hidden = True matching_chals = Challenges.query.filter_by( name=chal_dbobj.name, description=chal_dbobj.description, value=chal_dbobj.value, category=chal_dbobj.category, hidden=chal_dbobj.hidden ).all() for match in matching_chals: if 'tags' in chal: tags_db = [tag.tag for tag in Tags.query.add_columns('tag').filter_by(chal=match.id).all()] if all([tag not in tags_db for tag in chal['tags']]): continue if 'files' in chal: files_db = [f.location for f in Files.query.add_columns('location').filter_by(chal=match.id).all()] if len(files_db) != len(chal['files']): continue hashes = [] for file_db in files_db: with open(os.path.join(dst_attachments, file_db), 'r') as f: hash = hashlib.md5(f.read()).digest() hashes.append(hash) mismatch = False for file in chal['files']: filepath = os.path.join(os.path.dirname(in_file), file) with open(filepath, 'r') as f: hash = hashlib.md5(f.read()).digest() if hash in hashes: hashes.remove(hash) else: mismatch = True break if mismatch: continue flags_db = Keys.query.filter_by(chal=match.id).all() for flag in chal['flags']: for flag_db in flags_db: if flag['flag'] != flag_db.flag: continue if flag['type'] != flag_db.key_type: continue skip = True break if skip: print "Skipping {}: Duplicate challenge found in DB".format(chal['name'].encode('utf8')) continue print "Adding {}".format(chal['name'].encode('utf8')) db.session.add(chal_dbobj) db.session.commit() if 'tags' in chal: for tag in chal['tags']: tag_dbobj = Tags(chal_dbobj.id, tag) db.session.add(tag_dbobj) for flag in chal['flags']: flag_db = Keys(chal_dbobj.id, flag['flag'], flag['type']) db.session.add(flag_db) if 'files' in chal: for file in chal['files']: filename = os.path.basename(file) dst_filename = secure_filename(filename) dst_dir = None while not dst_dir or os.path.exists(dst_dir): md5hash = hashlib.md5(os.urandom(64)).hexdigest() dst_dir = os.path.join(dst_attachments, md5hash) os.makedirs(dst_dir) dstpath = os.path.join(dst_dir, dst_filename) srcpath = os.path.join(os.path.dirname(in_file), file) if move: shutil.move(srcpath, dstpath) else: shutil.copy(srcpath, dstpath) file_dbobj = Files(chal_dbobj.id, os.path.relpath(dstpath, start=dst_attachments)) db.session.add(file_dbobj) db.session.commit() db.session.close()
def setup(): if not config.is_setup(): if not session.get("nonce"): session["nonce"] = generate_nonce() if request.method == "POST": ctf_name = request.form["ctf_name"] set_config("ctf_name", ctf_name) # CSS set_config("start", "") # Admin user name = request.form["name"] password = request.form["password"] admin = Admins( name=name, password=password, type="admin", hidden=True ) user_mode = request.form["user_mode"] set_config("user_mode", user_mode) # Index page index = """<div class="row"> <div class="col-md-6 offset-md-3"> <img class="w-100 mx-auto d-block" style="max-width: 500px;padding: 50px;padding-top: 14vh;" src="themes/core/static/img/logo.png" /> <h3 class="text-center"> <p>A cool CTF platform from <a href="https://ctfd.io">ctfd.io</a></p> <p>Follow us on social media:</p> <a href="https://twitter.com/ctfdio"><i class="fab fa-twitter fa-2x" aria-hidden="true"></i></a> <a href="https://facebook.com/ctfdio"><i class="fab fa-facebook fa-2x" aria-hidden="true"></i></a> <a href="https://github.com/ctfd"><i class="fab fa-github fa-2x" aria-hidden="true"></i></a> </h3> <br> <h4 class="text-center"> <a href="admin">Click here</a> to login and setup your CTF </h4> </div> </div>""".format( request.script_root ) page = Pages(title=None, route="index", content=index, draft=False) # Visibility set_config("challenge_visibility", "private") set_config("registration_visibility", "public") set_config("score_visibility", "public") set_config("account_visibility", "public") # Start time set_config("start", None) set_config("end", None) set_config("freeze", None) set_config("setup", True) secret = Challenges(name='__SECRET__', value=500, type='standard', state='hidden') try: db.session.add(admin) db.session.commit() except IntegrityError: db.session.rollback() try: db.session.add(page) db.session.commit() except IntegrityError: db.session.rollback() try: db.session.add(secret) db.session.commit() except IntegrityError: db.session.rollback() login_user(admin) db.session.close() app.setup = False with app.app_context(): cache.clear() return redirect(url_for("views.static_html")) return render_template("setup.html", nonce=session.get("nonce")) return redirect(url_for("views.static_html"))
def import_challenges(in_file, dst_attachments, exit_on_error=True, move=False): from CTFd.models import db, Challenges, Flags, Tags, Hints, ChallengeFiles chals = [] with open(in_file, 'r') as in_stream: chals = yaml.safe_load_all(in_stream) for chal in chals: skip = False for req_field in REQ_FIELDS: if req_field not in chal: if exit_on_error: raise MissingFieldError(req_field) else: print("Skipping challenge: Missing field '{}'".format( req_field)) skip = True break if skip: continue for flag in chal['flags']: if 'flag' not in flag: if exit_on_error: raise MissingFieldError('flag') else: print("Skipping flag: Missing field 'flag'") continue flag['flag'] = flag['flag'].strip() if 'type' not in flag: flag['type'] = "static" if 'data' not in flag: flag['data'] = "" if 'files' in chal: norm_files = [] for file in chal['files']: # make sure we have only relative paths in the yaml file file = os.path.normpath("/" + file).lstrip('/') # skip files that do not exists if not os.path.exists( os.path.join(os.path.dirname(in_file), file)): print( "Skipping file '{}' in challenge '{}': File not found" .format(file, chal['name'].strip())) continue else: norm_files.append(file) chal['files'] = norm_files for hint in chal['hints']: if 'type' not in hint: hint['type'] = "standard" # Check what type the challenge is and create a DB object of the appropriate type. if chal['type'] == 'dynamic': # Lazy load the DynamicChallenge plugin on encountering a challenge of that type. try: from CTFd.plugins.dynamic_challenges import DynamicChallenge except ImportError as err: print("Failed to import plugin for challenge type {}: {}". format(chal['type'], err)) continue initial = int(chal['value']) if 'initial' in chal: initial = int(chal['initial']) minimum = 0 if 'minimum' in chal: minimum = int(chal['minimum']) decay = 0 if 'decay' in chal: decay = int(chal['decay']) chal_dbobj = DynamicChallenge( name=chal['name'].strip(), description=chal['description'].strip(), value=int(chal['value']), category=chal['category'].strip(), initial=initial, decay=decay, minimum=minimum) elif chal['type'] == 'naumachia': # Lazy load the Naumachia plugin on encountering a challenge of that type. try: # Here we use a fixed name, which is the repository name, even though it does # not conform to a proper Python package name. Users may install the package # using any file name they want, but this version of thsi plugin does not # support it. naumachia_plugin = importlib.import_module( '.ctfd-naumachia-plugin', package="CTFd.plugins") except ImportError as err: print("Failed to import plugin for challenge type {}: {}". format(chal['type'], err)) continue naumachia_name = chal.get('naumachia_name', "").strip(), chal_dbobj = naumachia_plugin.NaumachiaChallengeModel( name=chal['name'].strip(), naumachia_name=chal['naumachia_name'], description=chal['description'].strip(), value=int(chal['value']), category=chal['category'].strip(), ) else: # We ignore traling and leading whitespace when importing challenges chal_dbobj = Challenges( name=chal['name'].strip(), description=chal['description'].strip(), value=int(chal['value']), category=chal['category'].strip()) chal_dbobj.state = 'visible' if 'hidden' in chal and chal['hidden']: if bool(chal['hidden']): chal_dbobj.state = 'hidden' chal_dbobj.max_attempts = 0 if 'max_attempts' in chal and chal['max_attempts']: chal_dbobj.max_attempts = chal['max_attempts'] chal_dbobj.type = 'standard' if 'type' in chal and chal['type']: chal_dbobj.type = chal['type'] matching_chals = Challenges.query.filter_by( name=chal_dbobj.name, description=chal_dbobj.description, value=chal_dbobj.value, category=chal_dbobj.category, state=chal_dbobj.state, type=chal_dbobj.type).all() for match in matching_chals: if 'tags' in chal: tags_db = [ tag.tag for tag in Tags.query.add_columns('tag').filter_by( challenge_id=match.id).all() ] if all([tag not in tags_db for tag in chal['tags']]): continue if 'files' in chal: files_db = [ f.location for f in ChallengeFiles.query.add_columns( 'location').filter_by(challenge_id=match.id).all() ] if len(files_db) != len(chal['files']): continue hashes = [] for file_db in files_db: with open(os.path.join(dst_attachments, file_db), 'rb') as f: hash = hashlib.md5(f.read()).digest() hashes.append(hash) mismatch = False for file in chal['files']: filepath = os.path.join(os.path.dirname(in_file), file) with open(filepath, 'rb') as f: hash = hashlib.md5(f.read()).digest() if hash in hashes: hashes.remove(hash) else: mismatch = True break if mismatch: continue flags_db = Flags.query.filter_by(challenge_id=match.id).all() for flag in chal['flags']: for flag_db in flags_db: if flag['flag'] != flag_db.content: continue if flag['type'] != flag_db.type: continue skip = True break if skip: print("Skipping '{}': Duplicate challenge found in DB".format( chal['name'].encode('utf8'))) continue print("Adding {}".format(chal['name'].encode('utf8'))) db.session.add(chal_dbobj) db.session.commit() if 'tags' in chal: for tag in chal['tags']: tag_dbobj = Tags(chal_dbobj.id, tag) db.session.add(tag_dbobj) for flag in chal['flags']: flag_db = Flags(challenge_id=chal_dbobj.id, content=flag['flag'], type=flag['type'], data=flag['data']) db.session.add(flag_db) for hint in chal['hints']: hint_db = Hints(challenge_id=chal_dbobj.id, content=hint['hint'], type=hint['type'], cost=int(hint['cost'])) db.session.add(hint_db) if 'files' in chal: for file in chal['files']: filename = os.path.basename(file) dst_filename = secure_filename(filename) dst_dir = None while not dst_dir or os.path.exists(dst_dir): md5hash = hashlib.md5(os.urandom(64)).hexdigest() dst_dir = os.path.join(dst_attachments, md5hash) os.makedirs(dst_dir) dstpath = os.path.join(dst_dir, dst_filename) srcpath = os.path.join(os.path.dirname(in_file), file) if move: shutil.move(srcpath, dstpath) else: shutil.copy(srcpath, dstpath) file_dbobj = ChallengeFiles(challenge_id=chal_dbobj.id, location=os.path.relpath( dstpath, start=dst_attachments)) db.session.add(file_dbobj) db.session.commit() db.session.close()
def random_date(start, end): return start + datetime.timedelta( seconds=random.randint(0, int((end - start).total_seconds()))) if __name__ == '__main__': with app.app_context(): db = app.db # Generating Challenges print("GENERATING CHALLENGES") for x in range(CHAL_AMOUNT): word = gen_word() db.session.add(Challenges(word, gen_sentence(), gen_value(), gen_category())) db.session.commit() db.session.add(Keys(x + 1, word, 'static')) db.session.commit() # Generating Files print("GENERATING FILES") AMT_CHALS_WITH_FILES = int(CHAL_AMOUNT * (3.0 / 4.0)) for x in range(AMT_CHALS_WITH_FILES): chal = random.randint(1, CHAL_AMOUNT) filename = gen_file() md5hash = hashlib.md5(filename.encode('utf-8')).hexdigest() db.session.add(Files(chal, md5hash + '/' + filename)) db.session.commit()
def import_challenges(in_file, dst_attachments, move=False): from CTFd.models import db, Challenges, Flags, Tags, ChallengeFiles, Hints from CTFd.utils import uploads from CTFd.plugins.dynamic_challenges import DynamicChallenge, DynamicValueChallenge with open(in_file, "r") as in_stream: data = list(yaml.safe_load_all(in_stream)) if len(data) == 0 or data[0] is None or "challs" not in data[ 0] or data[0]["challs"] is None: raise ValueError("Invalid YAML format. Missing field 'challs'.") for chal in data[0]["challs"]: for req_field in REQ_FIELDS: if req_field not in chal: raise ValueError( "Invalid YAML format. Missing field '{0}'.".format( req_field)) if chal.get("type", "standard") == "dynamic": for req_field in ["minimum", "decay"]: if req_field not in chal: raise ValueError( "Invalid YAML format. Missing field '{0}'.".format( req_field)) if chal["flags"] is None: raise ValueError("Invalid YAML format. Missing field 'flag'.") for flag in chal["flags"]: if "flag" not in flag: raise ValueError( "Invalid YAML format. Missing field 'flag'.") flag["flag"] = flag["flag"].strip() if "type" not in flag: flag["type"] = "static" matching_chal = Challenges.query.filter_by( name=chal["name"].strip()).first() if matching_chal: print(("Updating {}: Duplicate challenge " "found in DB (id: {})").format( chal["name"].encode("utf8"), matching_chal.id)) Tags.query.filter_by(challenge_id=matching_chal.id).delete() ChallengeFiles.query.filter_by( challenge_id=matching_chal.id).delete() Flags.query.filter_by(challenge_id=matching_chal.id).delete() Hints.query.filter_by(challenge_id=matching_chal.id).delete() matching_chal.name = chal["name"].strip() matching_chal.description = chal["description"].strip() matching_chal.category = chal["category"].strip() if chal.get("type", "standard") == "standard": matching_chal.value = chal["value"] if chal.get("type", "standard") == "dynamic": matching_chal.minimum = chal["minimum"] matching_chal.decay = chal["decay"] matching_chal.initial = chal["value"] DynamicValueChallenge.calculate_value(matching_chal) db.session.commit() chal_dbobj = matching_chal else: print("Adding {}".format(chal["name"].encode("utf8"))) chal_type = chal.get("type", "standard") if chal_type == "standard": # We ignore traling and leading whitespace when # importing challenges chal_dbobj = Challenges( name=chal["name"].strip(), description=chal["description"].strip(), value=chal["value"], category=chal["category"].strip(), ) elif chal_type == "dynamic": # We ignore traling and leading whitespace when # importing challenges chal_dbobj = DynamicChallenge( name=chal["name"].strip(), description=chal["description"].strip(), category=chal["category"].strip(), value=int(chal["value"]), minimum=int(chal["minimum"]), decay=int(chal["decay"]), ) else: raise ValueError("Unknown type of challenge") db.session.add(chal_dbobj) db.session.commit() for tag in chal.get("tags", []): tag_dbobj = Tags(challenge_id=chal_dbobj.id, value=tag) db.session.add(tag_dbobj) for flag in chal["flags"]: flag_db = Flags(challenge_id=chal_dbobj.id, content=flag["flag"], type=flag["type"]) db.session.add(flag_db) for hint in chal.get("hints", []): hint_dbobj = Hints(challenge_id=chal_dbobj.id, content=hint["content"], cost=hint["cost"]) db.session.add(hint_dbobj) chal_dbobj.state = "hidden" if ( "hidden" in chal and chal["hidden"] == True) else "visible" chal_dbobj.max_attempts = chal[ "max_attempts"] if "max_attempts" in chal else 0 if "files" in chal: from io import FileIO for filename in chal["files"]: try: # upload_file takes a werkzeug.FileStorage object, but we # can get close enough with a file object with a # filename property added filepath = os.path.join(os.path.dirname(in_file), filename) with FileIO(filepath, mode="rb") as f: f.filename = os.path.basename(f.name) uploads.upload_file(file=f, challenge=chal_dbobj.id, type="challenge") except FileNotFoundError: raise ValueError( "Unable to import challenges. Missing file: " + filename) db.session.commit() # update challenge prerequisites after all the challenges were created with open(in_file, "r") as in_stream: data = list(yaml.safe_load_all(in_stream)) for chal in data[0]["challs"]: chal_dbobj = Challenges.query.filter_by( name=chal["name"].strip()).first() prerequisites = set() for prerequisite in chal.get("prerequisites", []): prerequisites.update([ c.id for c in Challenges.query.filter_by( name=prerequisite).all() ]) chal_dbobj.requirements = {"prerequisites": list(prerequisites)} db.session.commit() db.session.close()
def random_date(start, end): return start + datetime.timedelta( seconds=random.randint(0, int((end - start).total_seconds()))) if __name__ == '__main__': with app.app_context(): db = app.db # Generating Challenges print("GENERATING CHALLENGES") for x in range(CHAL_AMOUNT): word = gen_word() flags = [{'flag': word, 'type': 0}] db.session.add( Challenges(word, gen_sentence(), gen_value(), gen_category(), flags, random.randint(1, 4), None)) db.session.commit() db.session.add(Keys(x + 1, word, 0)) db.session.commit() # Generating Files print("GENERATING FILES") AMT_CHALS_WITH_FILES = int(CHAL_AMOUNT * (3.0 / 4.0)) for x in range(AMT_CHALS_WITH_FILES): chal = random.randint(1, CHAL_AMOUNT) filename = gen_file() md5hash = hashlib.md5(filename).hexdigest() db.session.add(Files(chal, md5hash + '/' + filename)) db.session.commit()
hint['type'] = "standard" to_update = [] if old_name := chal.get('old_name'): to_update = Challenges.query.filter_by(name=old_name).all() if not to_update: to_update = Challenges.query.filter_by(name=name).all() if not to_update: events.append(Event(Event.Type.info, f"Creating new challenge {name}")) if chal.get('type') == 'dynamic': chal_dbobj = DynamicChallenge(value=value) else: chal_dbobj = Challenges() # No challenge found with specified name, this is a new challenge else: if len(to_update) > 1: events.append(Event(Event.Type.warn, f"Challenge {name}: Found more than one challenge with the same name. Only the first one will be updated.")) events.append(Event(Event.Type.info, f"Updating challenge {name}")) chal_dbobj = to_update[0] chal_dbobj.name=name chal_dbobj.description=description chal_dbobj.value=value chal_dbobj.category=category chal_dbobj.connection_info = chal.get('connection_info') chal_dbobj.state = 'hidden' if chal.get('hidden') else 'visible'
def flaganizer_submit(): if authed() is False: return { "success": True, "data": { "status": "authentication_required" } }, 403 if request.content_type != "application/json": request_data = request.form else: request_data = request.get_json() if ctf_paused(): return ( { "success": True, "data": { "status": "paused", "message": "{} is paused".format(config.ctf_name()), }, }, 403, ) user = get_current_user() team = get_current_team() # TODO: Convert this into a re-useable decorator if config.is_teams_mode() and team is None: abort(403) kpm = current_user.get_wrong_submissions_per_minute(user.account_id) frsp = requests.post(FLAGANIZER_VERIFY_ENDPOINT, data={ "flag": request_data.get("submission", "") }, headers={ "X-CTFProxy-SubAcc-JWT": request.headers.get("X-CTFProxy-JWT") }).json() if frsp["Success"] == 0: if ctftime() or current_user.is_admin(): placeholder_challenge = Challenges.query.filter_by( name="wrong submission").first() if placeholder_challenge is None: placeholder_challenge = Challenges( name="wrong submission", description=FLAGANIZER_DESCRIPTION_PREFIX + "a placeholder challenge for unrecognized flags", value=0, category="misc", state="hidden", max_attempts=0) db.session.add(placeholder_challenge) db.session.commit() db.session.close() placeholder_challenge = Challenges.query.filter_by( name="wrong submission").first() chal_class = get_chal_class(placeholder_challenge.type) if placeholder_challenge is not None: chal_class.fail(user=user, team=team, challenge=placeholder_challenge, request=request) clear_standings() log( "submissions", "[{date}] {name} submitted {submission} via flaganizer with kpm {kpm} [WRONG]", submission=request_data.get("submission", "").encode("utf-8"), kpm=kpm, ) return { "success": True, "data": { "status": "incorrect", "message": frsp["Message"] }, } challenge = Challenges.query.filter_by( description=FLAGANIZER_DESCRIPTION_PREFIX + frsp["Flag"]["Id"]).first() if challenge is None: challenge = Challenges(name=frsp["Flag"]["DisplayName"], description=FLAGANIZER_DESCRIPTION_PREFIX + frsp["Flag"]["Id"], value=frsp["Flag"]["Points"], category=frsp["Flag"]["Category"], state="hidden", max_attempts=0) db.session.add(challenge) db.session.commit() challenge_id = challenge.id if challenge.state == "locked": db.session.close() abort(403) if challenge.requirements: requirements = challenge.requirements.get("prerequisites", []) 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([solve_id for solve_id, in solve_ids]) prereqs = set(requirements) if solve_ids >= prereqs: pass else: db.session.close() abort(403) chal_class = get_chal_class(challenge.type) if kpm > 10: if ctftime(): chal_class.fail(user=user, team=team, challenge=challenge, request=request) log( "submissions", "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [TOO FAST]", submission=request_data.get("submission", "").encode("utf-8"), challenge_id=challenge_id, kpm=kpm, ) # Submitting too fast db.session.close() return ( { "success": True, "data": { "status": "ratelimited", "message": "You're submitting flags too fast. Slow down.", }, }, 429, ) solves = Solves.query.filter_by(account_id=user.account_id, challenge_id=challenge_id).first() # Challenge not solved yet if not solves: status, message = chal_class.attempt(challenge, request) chal_class.solve(user=user, team=team, challenge=challenge, request=request) clear_standings() log( "submissions", "[{date}] {name} submitted {submission} on {challenge_id} via flaganizer with kpm {kpm} [CORRECT]", submission=request_data.get("submission", "").encode("utf-8"), challenge_id=challenge_id, kpm=kpm, ) db.session.close() return { "success": True, "data": { "status": "correct", "message": "Successfully submitted!" }, } else: log( "submissions", "[{date}] {name} submitted {submission} on {challenge_id} via flaganizer with kpm {kpm} [ALREADY SOLVED]", submission=request_data.get("submission", "").encode("utf-8"), challenge_id=challenge_id, kpm=kpm, ) db.session.close() return { "success": True, "data": { "status": "already_solved", "message": "You already solved this", }, }
def random_date(start, end): return start + datetime.timedelta( seconds=random.randint(0, int((end - start).total_seconds()))) if __name__ == '__main__': with app.app_context(): db = app.db # Generating Challenges print("GENERATING CHALLENGES") for x in range(1, len(challenge) + 1): db.session.add( Challenges(get_name(x), get_desc(x), get_value(x), get_category(x), get_hint(x))) db.session.commit() db.session.add(Keys(x, get_flag(x), 0)) db.session.commit() db.session.close() # Generating Users print("GENERATING USERS") used = [] count = 0 while count < USER_AMOUNT: name = gen_name() if name not in used: used.append(name) team = Teams(None, None, None, None, None, name, name.lower() + gen_email(), 'password', 's') team.verified = True
def import_challenges(in_file, dst_attachments, exit_on_error=True, move=False): from CTFd.models import db, Challenges, Keys, Tags, Files, Hints, Unlocks with open(in_file, 'r') as in_stream: chals = yaml.safe_load_all(in_stream) for chal in chals: # ensure all required fields are present before adding or updating a challenge try: validate_yaml(chal) except MissingFieldError as err: if exit_on_error: raise else: print "Skipping challenge: " + str(err) continue # if the challenge already exists, update it chal_db = Challenges.query.filter_by(name=chal['name']).first() if chal_db is not None: print "Updating {}".format(chal['name'].encode('utf8')) chal_db.description = chal['description'] chal_db.value = chal['value'] chal_db.category = chal['category'] else: print "Adding {}".format(chal['name'].encode('utf8')) chal_db = Challenges( chal['name'], chal['description'], chal['value'], chal['category']) chal_db.type = chal['type'] chal_db.hidden = chal['hidden'] db.session.add(chal_db) db.session.commit() # delete all tags and re-add them Tags.query.filter_by(chal=chal_db.id).delete() for tag in chal['tags']: tag_dbobj = Tags(chal_db.id, tag) db.session.add(tag_dbobj) # delete all flags and re-add them Keys.query.filter_by(chal=chal_db.id).delete() for flag in chal['flags']: flag_db = Keys(chal_db.id, flag['flag'], flag['type']) db.session.add(flag_db) # delete or update existing hints hints = {h['id']: h for h in chal['hints']} hints_db = Hints.query.filter_by(chal=chal_db.id).all() for hint_db in hints_db: if hint_db.type in hints: # the hint is being updated hint_db.hint = hints[hint_db.type]['hint'] hint_db.cost = hints[hint_db.type]['cost'] del hints[hint_db.type] else: # the hint is being deleted - delete the hint and any related unlocks print " Removing hint {:d}".format(hint_db.type) Unlocks.query.filter_by(model='hints', itemid=hint_db.id).delete() Hints.query.filter_by(id=hint_db.id).delete() # add new hints for hint in hints.values(): print " Adding hint {:d}".format(hint['id']) hint_db = Hints(chal_db.id, hint['hint'], cost=hint['cost'], type=hint['id']) db.session.add(hint_db) # hash and compare existing files with the new uploaded files hashes_db = {} files_db = Files.query.filter_by(chal=chal_db.id).all() for file_db in files_db: with open(os.path.join(dst_attachments, file_db.location), 'rb') as f: h = hashlib.md5(f.read()).digest() hashes_db[h] = file_db to_upload = [] for file in chal['files']: path = os.path.join(os.path.dirname(in_file), file) with open(path, "rb") as f: h = hashlib.md5(f.read()).digest() if h in hashes_db and os.path.basename(file) == os.path.basename(hashes_db[h].location): # the file is up to date del hashes_db[h] else: # the file has changed name or content to_upload.append(path) # remove out of date files and add new uploads for file_db in hashes_db.values(): print " Removing file {}".format(file_db.location) utils.delete_file(file_db.id) for path in to_upload: basename = os.path.basename(path) print " Adding file {}".format(basename) with open(path, "rb") as f: f = FileStorage(stream=f, filename=basename) utils.upload_file(file=f, chalid=chal_db.id) if move: os.unlink(path) db.session.commit() db.session.commit() db.session.close()
__name__ = 'CTFd' app = create_app() with app.app_context(): db = app.db data = json.load(open(filename)) # Clean before update db.session.query(Challenges).delete() for challenge in data["challenges"]: name = challenge["name"] message = challenge["message"] value = challenge["value"] category = challenge["category"] keys = challenge["key"] files = challenge["files"] challenge_obj = Challenges(name, message, value, category) db.session.add(challenge_obj) db.session.commit() for key in keys: key_obj = Keys(challenge_obj.id, key["flag"], key["type"]) db.session.add(key_obj) for path in files: db.session.add(Files(challenge_obj.id, path)) db.session.commit()
def admin_create_chal(): if request.method == 'POST': print("[DEBUG] Post request sent to my modified admin_create_chal") files = request.files.getlist('files[]') # Create challenge chal = Challenges( name=request.form['name'], description=request.form['desc'], value=request.form['value'], category=request.form['category'], type=request.form['chaltype'], ) if 'hidden' in request.form: chal.hidden = True else: chal.hidden = False max_attempts = request.form.get('max_attempts') if max_attempts and max_attempts.isdigit(): chal.max_attempts = int(max_attempts) db.session.add(chal) db.session.flush() # This if added by me print("[DEBUG] chal.id: " + str(chal.id)) if chal.type == 'ethereum': solidity = request.form['solidity'] test_func = request.form['test_func'] args = request.form['args'] starting_ether = request.form['starting-ether'] flag = request.form['key'] print("[DEBUG] Type is ethereum!") if ethereumctf.compile_contract(str(chal.id), solidity, test_func, ast.literal_eval(args), starting_ether, flag): print("[DEBUG] successful compile!") else: db.session.rollback() print("[DEBUG] failed compile") return redirect( url_for('admin_challenges.admin_create_chal') ) # TODO: Fail better db.session.commit() flag = Keys(chal.id, request.form['key'], int(request.form['key_type[0]'])) if request.form.get('keydata'): flag.data = request.form.get('keydata') db.session.add(flag) db.session.commit() for f in files: utils.upload_file(file=f, chalid=chal.id) db.session.commit() db.session.close() return redirect(url_for('admin_challenges.admin_chals')) else: return render_template('admin/chals/create.html')