Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
                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()