Beispiel #1
0
def test_ctf_ended():
    """
    Tests that the ctf_ended function returns the correct value
    """
    app = create_ctfd()
    with app.app_context():
        assert ctf_ended() is False
        with ctftime.init():

            with ctftime.not_started():
                assert ctf_ended() is False

            with ctftime.started():
                assert ctf_ended() is False

            with ctftime.ended():
                assert ctf_ended() is True
    destroy_ctfd(app)
Beispiel #2
0
    def during_ctf_time_only_wrapper(*args, **kwargs):
        if ctftime() or current_user.is_admin():
            return f(*args, **kwargs)
        else:
            if ctf_ended():
                error = '{} has ended'.format(config.ctf_name())
                abort(403, description=error)

            if ctf_started() is False:
                error = '{} has not started yet'.format(config.ctf_name())
                abort(403, description=error)
Beispiel #3
0
    def get(self, challenge_id):
        if is_admin():
            chal = Challenges.query.filter(Challenges.id == challenge_id).first_or_404()
        else:
            chal = Challenges.query.filter(
                Challenges.id == challenge_id, and_(Challenges.state != 'hidden', Challenges.state != 'locked')
            ).first_or_404()

        chal_class = get_chal_class(chal.type)

        tags = [
            tag['value'] for tag in TagSchema(
                "user", many=True).dump(
                chal.tags).data]
        files = [f.location for f in chal.files]

        unlocked_hints = set()
        hints = []
        if authed():
            user = get_current_user()
            unlocked_hints = set([u.target for u in HintUnlocks.query.filter_by(
                type='hints', account_id=user.account_id)])

        for hint in Hints.query.filter_by(challenge_id=chal.id).all():
            if hint.id in unlocked_hints or ctf_ended():
                hints.append({'id': hint.id, 'cost': hint.cost,
                              'content': hint.content})
            else:
                hints.append({'id': hint.id, 'cost': hint.cost})

        response = chal_class.read(challenge=chal)

        Model = get_model()

        if scores_visible() is True and accounts_visible() is True:
            solves = Solves.query\
                .join(Model, Solves.account_id == Model.id)\
                .filter(Solves.challenge_id == chal.id, Model.banned == False, Model.hidden == False)\
                .count()
            response['solves'] = solves
        else:
            response['solves'] = None

        response['files'] = files
        response['tags'] = tags
        response['hints'] = hints

        db.session.close()
        return {
            'success': True,
            'data': response
        }
Beispiel #4
0
 def during_ctf_time_only_wrapper(*args, **kwargs):
     if ctftime() or current_user.is_admin():
         return f(*args, **kwargs)
     else:
         if ctf_ended():
             if view_after_ctf():
                 return f(*args, **kwargs)
             else:
                 error = '{} 已经结束'.format(config.ctf_name())
                 abort(403, description=error)
         if ctf_started() is False:
             error = '{} 尚未开始'.format(config.ctf_name())
             abort(403, description=error)
Beispiel #5
0
def test_ctf_ended():
    """
    Tests that the ctf_ended function returns the correct value
    """
    app = create_ctfd()
    with app.app_context():
        assert ctf_ended() is False

        set_config("start", "1507089600"
                   )  # Wednesday, October 4, 2017 12:00:00 AM GMT-04:00 DST
        set_config(
            "end",
            "1507262400")  # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST

        with freeze_time("2017-10-3"):
            assert ctf_ended() is False

        with freeze_time("2017-10-5"):
            assert ctf_ended() is False

        with freeze_time("2017-10-7"):
            assert ctf_ended() is True
    destroy_ctfd(app)
Beispiel #6
0
def listing():
    infos = get_infos()
    errors = get_errors()

    if ctf_started() is False:
        errors.append(f"{config.ctf_name()} has not started yet")

    if ctf_paused() is True:
        infos.append(f"{config.ctf_name()} is paused")

    if ctf_ended() is True:
        infos.append(f"{config.ctf_name()} has ended")

    return render_template("challenges.html", infos=infos, errors=errors)
Beispiel #7
0
def listing():
    infos = get_infos()
    errors = get_errors()

    if ctf_started() is False:
        errors.append(f"{config.ctf_name()} ещё не начался")

    if ctf_paused() is True:
        infos.append(f"{config.ctf_name()} приостановлен")

    if ctf_ended() is True:
        infos.append(f"{config.ctf_name()} закончился")

    return render_template("challenges.html", infos=infos, errors=errors)
Beispiel #8
0
def listing():
    infos = get_infos()
    errors = get_errors()
    start = get_config("start") or 0
    end = get_config("end") or 0

    if ctf_paused():
        infos.append("{} is paused".format(config.ctf_name()))

    # CTF has ended but we want to allow view_after_ctf. Show error but let JS load challenges.
    if ctf_ended() and view_after_ctf():
        infos.append("{} has ended".format(config.ctf_name()))

    return render_template(
        "challenges.html", infos=infos, errors=errors, start=int(start), end=int(end)
    )
Beispiel #9
0
def static_html(route):
    """
    Route in charge of routing users to Pages.
    :param route:
    :return:
    """
    page = get_page(route)
    if page is None:
        if (ctftime() or current_user.is_admin()
                or (ctf_ended() and view_after_ctf())):
            filename = safe_join(app.root_path, "static", route)
            if os.path.isfile(filename):
                return send_file(filename)
        abort(404)
    else:
        if page.auth_required and authed() is False:
            return redirect(url_for("auth.login", next=request.full_path))

        return render_template("page.html", content=markdown(page.content))
Beispiel #10
0
def listing():
    if (Configs.challenge_visibility == ChallengeVisibilityTypes.PUBLIC
            and authed() is False):
        pass
    else:
        if is_teams_mode() and get_current_team() is None:
            return redirect(url_for("teams.private", next=request.full_path))

    infos = get_infos()
    errors = get_errors()

    if ctf_started() is False:
        errors.append(f"{Configs.ctf_name} has not started yet")

    if ctf_paused() is True:
        infos.append(f"{Configs.ctf_name} is paused")

    if ctf_ended() is True:
        infos.append(f"{Configs.ctf_name} has ended")

    return render_template("challenges.html", infos=infos, errors=errors)
Beispiel #11
0
    def get(self, challenge_id):
        if is_admin():
            chal = Challenges.query.filter(Challenges.id == challenge_id).first_or_404()
        else:
            chal = Challenges.query.filter(
                Challenges.id == challenge_id,
                and_(Challenges.state != "hidden", Challenges.state != "locked"),
            ).first_or_404()

        chal_class = get_chal_class(chal.type)

        if chal.requirements:
            requirements = chal.requirements.get("prerequisites", [])
            anonymize = chal.requirements.get("anonymize")
            if challenges_visible():
                user = get_current_user()
                if user:
                    solve_ids = (
                        Solves.query.with_entities(Solves.challenge_id)
                        .filter_by(account_id=user.account_id)
                        .order_by(Solves.challenge_id.asc())
                        .all()
                    )
                else:
                    # We need to handle the case where a user is viewing challenges anonymously
                    solve_ids = []
                solve_ids = set([value for value, in solve_ids])
                prereqs = set(requirements)
                if solve_ids >= prereqs or is_admin():
                    pass
                else:
                    if anonymize:
                        return {
                            "success": True,
                            "data": {
                                "id": chal.id,
                                "type": "hidden",
                                "name": "???",
                                "value": 0,
                                "category": "???",
                                "tags": [],
                                "template": "",
                                "script": "",
                            },
                        }
                    abort(403)
            else:
                abort(403)

        tags = [
            tag["value"] for tag in TagSchema("user", many=True).dump(chal.tags).data
        ]

        unlocked_hints = set()
        hints = []
        if authed():
            user = get_current_user()
            team = get_current_team()

            # TODO: Convert this into a re-useable decorator
            if is_admin():
                pass
            else:
                if config.is_teams_mode() and team is None:
                    abort(403)

            unlocked_hints = set(
                [
                    u.target
                    for u in HintUnlocks.query.filter_by(
                        type="hints", account_id=user.account_id
                    )
                ]
            )
            files = []
            for f in chal.files:
                token = {
                    "user_id": user.id,
                    "team_id": team.id if team else None,
                    "file_id": f.id,
                }
                files.append(
                    url_for("views.files", path=f.location, token=serialize(token))
                )
        else:
            files = [url_for("views.files", path=f.location) for f in chal.files]

        for hint in Hints.query.filter_by(challenge_id=chal.id).all():
            if hint.id in unlocked_hints or ctf_ended():
                hints.append(
                    {"id": hint.id, "cost": hint.cost, "content": hint.content}
                )
            else:
                hints.append({"id": hint.id, "cost": hint.cost})

        response = chal_class.read(challenge=chal)

        Model = get_model()

        if scores_visible() is True and accounts_visible() is True:
            solves = Solves.query.join(Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == chal.id,
                Model.banned == False,
                Model.hidden == False,
            )

            # Only show solves that happened before freeze time if configured
            freeze = get_config("freeze")
            if not is_admin() and freeze:
                solves = solves.filter(Solves.date < unix_time_to_utc(freeze))

            solves = solves.count()
            response["solves"] = solves
        else:
            response["solves"] = None

        response["files"] = files
        response["tags"] = tags
        response["hints"] = hints

        db.session.close()
        return {"success": True, "data": response}
Beispiel #12
0
    def get(self, challenge_id):
        if is_admin():
            chal = Challenges.query.filter(
                Challenges.id == challenge_id).first_or_404()
        else:
            chal = Challenges.query.filter(
                Challenges.id == challenge_id,
                and_(Challenges.state != "hidden",
                     Challenges.state != "locked"),
            ).first_or_404()

        try:
            chal_class = get_chal_class(chal.type)
        except KeyError:
            abort(
                500,
                f"The underlying challenge type ({chal.type}) is not installed. This challenge can not be loaded.",
            )

        if chal.requirements:
            requirements = chal.requirements.get("prerequisites", [])
            anonymize = chal.requirements.get("anonymize")
            # Gather all challenge IDs so that we can determine invalid challenge prereqs
            all_challenge_ids = {
                c.id
                for c in Challenges.query.with_entities(Challenges.id).all()
            }
            if challenges_visible():
                user = get_current_user()
                if user:
                    solve_ids = (Solves.query.with_entities(
                        Solves.challenge_id).filter_by(
                            account_id=user.account_id).order_by(
                                Solves.challenge_id.asc()).all())
                else:
                    # We need to handle the case where a user is viewing challenges anonymously
                    solve_ids = []
                solve_ids = {value for value, in solve_ids}
                prereqs = set(requirements).intersection(all_challenge_ids)
                if solve_ids >= prereqs or is_admin():
                    pass
                else:
                    if anonymize:
                        return {
                            "success": True,
                            "data": {
                                "id": chal.id,
                                "type": "hidden",
                                "name": "???",
                                "value": 0,
                                "solves": None,
                                "solved_by_me": False,
                                "category": "???",
                                "tags": [],
                                "template": "",
                                "script": "",
                            },
                        }
                    abort(403)
            else:
                abort(403)

        tags = [
            tag["value"]
            for tag in TagSchema("user", many=True).dump(chal.tags).data
        ]

        unlocked_hints = set()
        hints = []
        if authed():
            user = get_current_user()
            team = get_current_team()

            # TODO: Convert this into a re-useable decorator
            if is_admin():
                pass
            else:
                if config.is_teams_mode() and team is None:
                    abort(403)

            unlocked_hints = {
                u.target
                for u in HintUnlocks.query.filter_by(
                    type="hints", account_id=user.account_id)
            }
            files = []
            for f in chal.files:
                token = {
                    "user_id": user.id,
                    "team_id": team.id if team else None,
                    "file_id": f.id,
                }
                files.append(
                    url_for("views.files",
                            path=f.location,
                            token=serialize(token)))
        else:
            files = [
                url_for("views.files", path=f.location) for f in chal.files
            ]

        for hint in Hints.query.filter_by(challenge_id=chal.id).all():
            if hint.id in unlocked_hints or ctf_ended():
                hints.append({
                    "id": hint.id,
                    "cost": hint.cost,
                    "content": hint.content
                })
            else:
                hints.append({"id": hint.id, "cost": hint.cost})

        response = chal_class.read(challenge=chal)

        solves_q, user_solves = _build_solves_query(
            extra_filters=(Solves.challenge_id == chal.id, ))
        # If there are no solves for this challenge ID then we have 0 rows
        maybe_row = solves_q.first()
        if maybe_row:
            challenge_id, solve_count = maybe_row
            solved_by_user = challenge_id in user_solves
        else:
            solve_count, solved_by_user = 0, False

        # Hide solve counts if we are hiding solves/accounts
        if scores_visible() is False or accounts_visible() is False:
            solve_count = None

        if authed():
            # Get current attempts for the user
            attempts = Submissions.query.filter_by(
                account_id=user.account_id, challenge_id=challenge_id).count()
        else:
            attempts = 0

        response["solves"] = solve_count
        response["solved_by_me"] = solved_by_user
        response["attempts"] = attempts
        response["files"] = files
        response["tags"] = tags
        response["hints"] = hints

        response["view"] = render_template(
            chal_class.templates["view"].lstrip("/"),
            solves=solve_count,
            solved_by_me=solved_by_user,
            files=files,
            tags=tags,
            hints=[Hints(**h) for h in hints],
            max_attempts=chal.max_attempts,
            attempts=attempts,
            challenge=chal,
        )

        db.session.close()
        return {"success": True, "data": response}
Beispiel #13
0
def files(path):
    """
    Route in charge of dealing with making sure that CTF challenges are only accessible during the competition.
    :param path:
    :return:
    """
    f = Files.query.filter_by(location=path).first_or_404()
    if f.type == "challenge":
        if challenges_visible():
            if current_user.is_admin() is False:
                if not ctftime():
                    if ctf_ended() and view_after_ctf():
                        pass
                    else:
                        abort(403)
        else:
            if not ctftime():
                abort(403)

            # Allow downloads if a valid token is provided
            token = request.args.get("token", "")
            try:
                data = unserialize(token, max_age=3600)
                user_id = data.get("user_id")
                team_id = data.get("team_id")
                file_id = data.get("file_id")
                user = Users.query.filter_by(id=user_id).first()
                team = Teams.query.filter_by(id=team_id).first()

                # Check user is admin if challenge_visibility is admins only
                if (
                    get_config(ConfigTypes.CHALLENGE_VISIBILITY) == "admins"
                    and user.type != "admin"
                ):
                    abort(403)

                # Check that the user exists and isn't banned
                if user:
                    if user.banned:
                        abort(403)
                else:
                    abort(403)

                # Check that the team isn't banned
                if team:
                    if team.banned:
                        abort(403)
                else:
                    pass

                # Check that the token properly refers to the file
                if file_id != f.id:
                    abort(403)

            # The token isn't expired or broken
            except (BadTimeSignature, SignatureExpired, BadSignature):
                abort(403)

    uploader = get_uploader()
    try:
        return uploader.download(f.location)
    except IOError:
        abort(404)
Beispiel #14
0
    def get(self, challenge_id):
        if is_admin():
            chal = Challenges.query.filter(
                Challenges.id == challenge_id).first_or_404()
        else:
            chal = Challenges.query.filter(
                Challenges.id == challenge_id,
                and_(Challenges.state != 'hidden',
                     Challenges.state != 'locked')).first_or_404()

        chal_class = get_chal_class(chal.type)

        if chal.requirements:
            requirements = chal.requirements.get('prerequisites', [])
            anonymize = chal.requirements.get('anonymize')
            if challenges_visible():
                user = get_current_user()
                if user:
                    solve_ids = Solves.query \
                        .with_entities(Solves.challenge_id) \
                        .filter_by(account_id=user.account_id) \
                        .order_by(Solves.challenge_id.asc()) \
                        .all()
                else:
                    # We need to handle the case where a user is viewing challenges anonymously
                    solve_ids = []
                solve_ids = set([value for value, in solve_ids])
                prereqs = set(requirements)
                if solve_ids >= prereqs or is_admin():
                    pass
                else:
                    if anonymize:
                        return {
                            'success': True,
                            'data': {
                                'id': chal.id,
                                'type': 'hidden',
                                'name': '???',
                                'value': 0,
                                'category': '???',
                                'tags': [],
                                'template': '',
                                'script': ''
                            }
                        }
                    abort(403)
            else:
                abort(403)

        tags = [
            tag['value']
            for tag in TagSchema("user", many=True).dump(chal.tags).data
        ]
        files = [f.location for f in chal.files]

        unlocked_hints = set()
        hints = []
        if authed():
            user = get_current_user()
            unlocked_hints = set([
                u.target for u in HintUnlocks.query.filter_by(
                    type='hints', account_id=user.account_id)
            ])

        for hint in Hints.query.filter_by(challenge_id=chal.id).all():
            if hint.id in unlocked_hints or ctf_ended():
                hints.append({
                    'id': hint.id,
                    'cost': hint.cost,
                    'content': hint.content
                })
            else:
                hints.append({'id': hint.id, 'cost': hint.cost})

        response = chal_class.read(challenge=chal)

        Model = get_model()

        if scores_visible() is True and accounts_visible() is True:
            solves = Solves.query\
                .join(Model, Solves.account_id == Model.id)\
                .filter(Solves.challenge_id == chal.id, Model.banned == False, Model.hidden == False)\
                .count()
            response['solves'] = solves
        else:
            response['solves'] = None

        response['files'] = files
        response['tags'] = tags
        response['hints'] = hints

        db.session.close()
        return {'success': True, 'data': response}