def gen_file(db, location, challenge_id=None, page_id=None): if challenge_id: f = ChallengeFiles(challenge_id=challenge_id, location=location) elif page_id: f = PageFiles(page_id=page_id, location=location) else: f = Files(location=location) db.session.add(f) db.session.commit() return f
def update_files(files, chal_dbobj, dst_attachments): events = [] from CTFd.models import db, ChallengeFiles db_file_objects = {Path(file.location).name:file for file in ChallengeFiles.query.filter_by(challenge_id=chal_dbobj.id).all()} chal_files = {secure_filename(Path(file).name):file for file in files} files_db = set(db_file_objects.keys()) files_in = set(chal_files.keys()) new_files = files_in - files_db del_files = files_db - files_in upd_files = files_in & files_db for file in upd_files: file_db = db_file_objects[file] old = Path(dst_attachments) / file_db.location new = chal_files[file] if file_md5(old) != file_md5(new): events.append(Event(Event.Type.info, f"Challenge {chal_dbobj.name}: Updating existing file {new.name}")) shutil.copy(new, old) for file in del_files: file_db = db_file_objects[file] path = Path(dst_attachments) / file_db.location path.unlink(missing_ok=True) # Delete containing directory if it is not emtpy try: path.parent.rmdir() except OSError: # Directory was not empty, ignore pass db.session.delete(file_db) events.append(Event(Event.Type.warn, f"Challenge {chal_dbobj.name}: Deleting file {path.name}")) for file in new_files: path = chal_files[file] safe_name = secure_filename(path.name) out = get_random_dir(dst_attachments) / safe_name out.parent.mkdir(exist_ok=True) shutil.copy(path, out) file_db = ChallengeFiles(challenge_id=chal_dbobj.id, location=str(out.relative_to(dst_attachments))) db.session.add(file_db) events.append(Event(Event.Type.info, f"Challenge {chal_dbobj.name}: Adding new file {safe_name}")) return events
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() chal_file = ChallengeFiles(challenge_id=chal, location=md5hash + "/" + filename) db.session.add(chal_file) db.session.commit() # Generating Teams print("GENERATING TEAMS") used = [] used_oauth_ids = [] count = 0 while count < TEAM_AMOUNT: name = gen_team_name() if name not in used: used.append(name) team = Teams(name=name, password="******") if random_chance():
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()