Esempio n. 1
0
    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()
Esempio n. 2
0
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
Esempio n. 3
0
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'))
Esempio n. 5
0
    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')
Esempio n. 6
0
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')
Esempio n. 7
0
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()
Esempio n. 8
0
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()
Esempio n. 9
0
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()
Esempio n. 11
0
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>&nbsp;
            <a href="https://facebook.com/ctfdio"><i class="fab fa-facebook fa-2x" aria-hidden="true"></i></a>&nbsp;
            <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()
Esempio n. 13
0

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()
Esempio n. 15
0
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()
Esempio n. 16
0
                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'
Esempio n. 17
0
    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",
                },
            }
Esempio n. 18
0

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()
Esempio n. 20
0
    __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()
Esempio n. 21
0
    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')