コード例 #1
0
ファイル: test_dynamic.py プロジェクト: ccns/CCNS-CTF
def test_can_delete_dynamic_challenge():
    app = create_ctfd(enable_plugins=True)
    with app.app_context():
        register_user(app)
        client = login_as_user(app, name="admin", password="******")

        challenge_data = {
            "name": "name",
            "category": "category",
            "description": "description",
            "value": 100,
            "decay": 20,
            "minimum": 1,
            "state": "hidden",
            "type": "dynamic",
        }

        r = client.post("/api/v1/challenges", json=challenge_data)
        assert r.get_json().get("data")["id"] == 1

        challenges = DynamicChallenge.query.all()
        assert len(challenges) == 1

        challenge = challenges[0]
        DynamicValueChallenge.delete(challenge)

        challenges = DynamicChallenge.query.all()
        assert len(challenges) == 0
    destroy_ctfd(app)
コード例 #2
0
ファイル: test_dynamic.py プロジェクト: ccns/CCNS-CTF
def test_dynamic_challenge_doesnt_lose_value_on_update():
    """Dynamic challenge updates without changing any values or solves shouldn't change the current value. See #1043"""
    app = create_ctfd(enable_plugins=True)
    with app.app_context():
        challenge_data = {
            "name": "name",
            "category": "category",
            "description": "description",
            "value": 10000,
            "decay": 4,
            "minimum": 10,
            "state": "visible",
            "type": "dynamic",
        }
        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.create(req)
        challenge_id = challenge.id
        gen_flag(app.db, challenge_id=challenge.id, content="flag")
        register_user(app)
        with login_as_user(app) as client:
            data = {"submission": "flag", "challenge_id": challenge_id}
            r = client.post("/api/v1/challenges/attempt", json=data)
            assert r.status_code == 200
            assert r.get_json()["data"]["status"] == "correct"
        chal = Challenges.query.filter_by(id=challenge_id).first()
        prev_chal_value = chal.value
        chal = DynamicValueChallenge.update(chal, req)
        assert prev_chal_value == chal.value
    destroy_ctfd(app)
コード例 #3
0
    def solve(cls, user, team, challenge, request):
        super().solve(user, team, challenge, request)

        if challenge.dynamic_score == 1:
            DynamicValueChallenge.calculate_value(challenge)

        db.session.commit()
コード例 #4
0
ファイル: test_dynamic.py プロジェクト: mrigank-9594/srhctf
def test_can_add_requirement_dynamic_challenge():
    """Test that requirements can be added to dynamic challenges"""
    app = create_ctfd(enable_plugins=True)
    with app.app_context():
        challenge_data = {
            "name": "name",
            "category": "category",
            "description": "description",
            "value": 100,
            "decay": 20,
            "minimum": 1,
            "state": "hidden",
            "type": "dynamic"
        }
        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.create(req)

        assert challenge.value == 100
        assert challenge.initial == 100
        assert challenge.decay == 20
        assert challenge.minimum == 1

        challenge_data = {
            "name": "second_name",
            "category": "category",
            "description": "new_description",
            "value": "200",
            "initial": "200",
            "decay": "40",
            "minimum": "5",
            "max_attempts": "0",
            "state": "visible"
        }

        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.create(req)

        assert challenge.name == 'second_name'
        assert challenge.description == "new_description"
        assert challenge.value == 200
        assert challenge.initial == 200
        assert challenge.decay == 40
        assert challenge.minimum == 5
        assert challenge.state == "visible"

        challenge_data = {
            "requirements": [1]
        }

        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.update(challenge, req)

        assert challenge.requirements == [1]

    destroy_ctfd(app)
コード例 #5
0
    def solve(cls, user, team, challenge, request):
        """
        This method is used to insert Solves into the database in order to mark a challenge as solved.

        :param team: The Team object from the database
        :param chal: The Challenge object from the database
        :param request: The request the user submitted
        :return:
        """
        super().solve(user, team, challenge, request)

        if challenge.dynamic_score == 1:
            DynamicValueChallenge.calculate_value(challenge)

        db.session.commit()
        db.session.close()
コード例 #6
0
ファイル: test_dynamic.py プロジェクト: KaitoRyouga/CTFd
def test_can_update_dynamic_challenge():
    app = create_ctfd(enable_plugins=True)
    with app.app_context():
        challenge_data = {
            "name": "name",
            "category": "category",
            "description": "description",
            "value": 100,
            "decay": 20,
            "minimum": 1,
            "state": "hidden",
            "type": "dynamic",
        }
        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.create(req)

        assert challenge.value == 100
        assert challenge.initial == 100
        assert challenge.decay == 20
        assert challenge.minimum == 1

        challenge_data = {
            "name": "new_name",
            "category": "category",
            "description": "new_description",
            "value": "200",
            "initial": "200",
            "decay": "40",
            "minimum": "5",
            "max_attempts": "0",
            "state": "visible",
        }

        req = FakeRequest(form=challenge_data)
        challenge = DynamicValueChallenge.update(challenge, req)

        assert challenge.name == "new_name"
        assert challenge.description == "new_description"
        assert challenge.value == 200
        assert challenge.initial == 200
        assert challenge.decay == 40
        assert challenge.minimum == 5
        assert challenge.state == "visible"

    destroy_ctfd(app)
コード例 #7
0
    def update(cls, challenge, request):
        data = request.form or request.get_json()

        for attr, value in data.items():
            # We need to set these to floats so that the next operations don't operate on strings
            if attr in ("initial", "minimum", "decay"):
                value = float(value)
            setattr(challenge, attr, value)

        if challenge.dynamic_score == 1:
            return DynamicValueChallenge.calculate_value(challenge)

        db.session.commit()
        return challenge
コード例 #8
0
    def update(cls, challenge, request):
        """
        This method is used to update the information associated with a challenge. This should be kept strictly to the
        Challenges table and any child tables.

        :param challenge:
        :param request:
        :return:
        """
        data = request.form or request.get_json()

        for attr, value in data.items():
            # We need to set these to floats so that the next operations don't operate on strings
            if attr in ("initial", "minimum", "decay"):
                value = float(value)
            setattr(challenge, attr, value)

        if challenge.dynamic_score == 1:
            return DynamicValueChallenge.calculate_value(challenge)

        db.session.commit()
        return challenge
コード例 #9
0
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()