Exemplo n.º 1
0
def confirm(data=None):
    if not get_config("verify_emails"):
        # If the CTF doesn't care about confirming email addresses then redierct to challenges
        return redirect(url_for("challenges.listing"))

    # User is confirming email account
    if data and request.method == "GET":
        try:
            user_email = unserialize(data, max_age=1800)
        except (BadTimeSignature, SignatureExpired):
            return render_template(
                "confirm.html", errors=["Your confirmation link has expired"])
        except (BadSignature, TypeError, base64.binascii.Error):
            return render_template(
                "confirm.html", errors=["Your confirmation token is invalid"])

        user = Users.query.filter_by(email=user_email).first_or_404()
        if user.verified:
            return redirect(url_for("views.settings"))

        user.verified = True
        log(
            "registrations",
            format="[{date}] {ip} - successful confirmation for {name}",
            name=user.name,
        )
        db.session.commit()
        email.successful_registration_notification(user.email)
        db.session.close()
        if current_user.authed():
            return redirect(url_for("challenges.listing"))
        return redirect(url_for("auth.login"))

    # User is trying to start or restart the confirmation flow
    if not current_user.authed():
        return redirect(url_for("auth.login"))

    user = Users.query.filter_by(id=session["id"]).first_or_404()
    if user.verified:
        return redirect(url_for("views.settings"))

    if data is None:
        if request.method == "POST":
            # User wants to resend their confirmation email
            email.verify_email_address(user.email)
            log(
                "registrations",
                format=
                "[{date}] {ip} - {name} initiated a confirmation email resend",
            )
            return render_template(
                "confirm.html",
                user=user,
                infos=["Your confirmation email has been resent!"],
            )
        elif request.method == "GET":
            # User has been directed to the confirm page
            return render_template("confirm.html", user=user)
Exemplo n.º 2
0
    def _check_score_visibility(*args, **kwargs):
        v = get_config("score_visibility")
        if v == "public":
            return f(*args, **kwargs)

        elif v == "private":
            if authed():
                return f(*args, **kwargs)
            else:
                if request.content_type == "application/json":
                    abort(403)
                else:
                    return redirect(
                        url_for("auth.login", next=request.full_path))

        elif v == "hidden":
            return (
                render_template("errors/403.html",
                                error="Scores are currently hidden"),
                403,
            )

        elif v == "admins":
            if is_admin():
                return f(*args, **kwargs)
            else:
                abort(404)
Exemplo n.º 3
0
def challenges_visible():
    v = get_config("challenge_visibility")
    if v == "public":
        return True
    elif v == "private":
        return authed()
    elif v == "admins":
        return is_admin()
Exemplo n.º 4
0
def accounts_visible():
    v = get_config("account_visibility")
    if v == "public":
        return True
    elif v == "private":
        return authed()
    elif v == "admins":
        return is_admin()
Exemplo n.º 5
0
 def authed_only_wrapper(*args, **kwargs):
     if authed():
         return f(*args, **kwargs)
     else:
         if (request.content_type == "application/json"
                 or request.accept_mimetypes.best == "text/event-stream"):
             abort(403)
         else:
             return redirect(url_for("auth.login", next=request.full_path))
Exemplo n.º 6
0
def scores_visible():
    v = get_config("score_visibility")
    if v == "public":
        return True
    elif v == "private":
        return authed()
    elif v == "hidden":
        return False
    elif v == "admins":
        return is_admin()
Exemplo n.º 7
0
    def tracker():
        if request.endpoint == "views.themes":
            return

        if authed():
            track = Tracking.query.filter_by(ip=get_ip(),
                                             user_id=session["id"]).first()
            if not track:
                visit = Tracking(ip=get_ip(), user_id=session["id"])
                db.session.add(visit)
            else:
                track.date = datetime.datetime.utcnow()

            try:
                db.session.commit()
            except (InvalidRequestError, IntegrityError):
                db.session.rollback()
                logout_user()

            if authed():
                user = get_current_user()
                team = get_current_team()

                if request.path.startswith("/themes") is False:
                    if user and user.banned:
                        return (
                            render_template(
                                "errors/403.html",
                                error="You have been banned from this CTF",
                            ),
                            403,
                        )

                    if team and team.banned:
                        return (
                            render_template(
                                "errors/403.html",
                                error="Your team has been banned from this CTF",
                            ),
                            403,
                        )

            db.session.close()
Exemplo n.º 8
0
 def _require_verified_emails(*args, **kwargs):
     if get_config("verify_emails"):
         if current_user.authed():
             if (current_user.is_admin() is False
                     and current_user.is_verified() is
                     False):  # User is not confirmed
                 if request.content_type == "application/json":
                     abort(403)
                 else:
                     return redirect(url_for("auth.confirm"))
     return f(*args, **kwargs)
Exemplo n.º 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:
        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))
Exemplo n.º 10
0
    def _check_challenge_visibility(*args, **kwargs):
        v = get_config("challenge_visibility")
        if v == "public":
            return f(*args, **kwargs)

        elif v == "private":
            if authed():
                return f(*args, **kwargs)
            else:
                if request.content_type == "application/json":
                    abort(403)
                else:
                    return redirect(
                        url_for("auth.login", next=request.full_path))

        elif v == "admins":
            if is_admin():
                return f(*args, **kwargs)
            else:
                if authed():
                    abort(403)
                else:
                    return redirect(
                        url_for("auth.login", next=request.full_path))
Exemplo n.º 11
0
    def post(self):
        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()

        challenge_id = request_data.get("challenge_id")

        if current_user.is_admin():
            preview = request.args.get("preview", False)
            if preview:
                challenge = Challenges.query.filter_by(
                    id=challenge_id).first_or_404()
                chal_class = get_chal_class(challenge.type)
                status, message = chal_class.attempt(challenge, request)

                return {
                    "success": True,
                    "data": {
                        "status": "correct" if status else "incorrect",
                        "message": message,
                    },
                }

        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)

        fails = Fails.query.filter_by(account_id=user.account_id,
                                      challenge_id=challenge_id).count()

        challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()

        if challenge.state == "hidden":
            abort(404)

        if challenge.state == "locked":
            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:
                abort(403)

        chal_class = get_chal_class(challenge.type)

        # Anti-bruteforce / submitting Flags too quickly
        kpm = current_user.get_wrong_submissions_per_minute(user.account_id)
        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["submission"].encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            # Submitting too fast
            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:
            # Hit max attempts
            max_tries = challenge.max_attempts
            if max_tries and fails >= max_tries > 0:
                return (
                    {
                        "success": True,
                        "data": {
                            "status": "incorrect",
                            "message": "You have 0 tries remaining",
                        },
                    },
                    403,
                )

            status, message = chal_class.attempt(challenge, request)
            if status:  # The challenge plugin says the input is right
                if ctftime() or current_user.is_admin():
                    chal_class.solve(user=user,
                                     team=team,
                                     challenge=challenge,
                                     request=request)
                    clear_standings()

                log(
                    "submissions",
                    "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [CORRECT]",
                    submission=request_data["submission"].encode("utf-8"),
                    challenge_id=challenge_id,
                    kpm=kpm,
                )
                return {
                    "success": True,
                    "data": {
                        "status": "correct",
                        "message": message
                    },
                }
            else:  # The challenge plugin says the input is wrong
                if ctftime() or current_user.is_admin():
                    chal_class.fail(user=user,
                                    team=team,
                                    challenge=challenge,
                                    request=request)
                    clear_standings()

                log(
                    "submissions",
                    "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [WRONG]",
                    submission=request_data["submission"].encode("utf-8"),
                    challenge_id=challenge_id,
                    kpm=kpm,
                )

                if max_tries:
                    # Off by one since fails has changed since it was gotten
                    attempts_left = max_tries - fails - 1
                    tries_str = "tries"
                    if attempts_left == 1:
                        tries_str = "try"
                    # Add a punctuation mark if there isn't one
                    if message[-1] not in "!().;?[]{}":
                        message = message + "."
                    return {
                        "success": True,
                        "data": {
                            "status":
                            "incorrect",
                            "message":
                            "{} You have {} {} remaining.".format(
                                message, attempts_left, tries_str),
                        },
                    }
                else:
                    return {
                        "success": True,
                        "data": {
                            "status": "incorrect",
                            "message": message
                        },
                    }

        # Challenge already solved
        else:
            log(
                "submissions",
                "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [ALREADY SOLVED]",
                submission=request_data["submission"].encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            return {
                "success": True,
                "data": {
                    "status": "already_solved",
                    "message": "You already solved this",
                },
            }
Exemplo n.º 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()

        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}
Exemplo n.º 13
0
 def __require_authentication_if_config(*args, **kwargs):
     value = get_config(config_key)
     if value and current_user.authed():
         return redirect(url_for("auth.login", next=request.full_path))
     else:
         return f(*args, **kwargs)
Exemplo n.º 14
0
def logout():
    if current_user.authed():
        logout_user()
    return redirect(url_for("views.static_html"))