Beispiel #1
0
    def get(self, challenge_id):
        response = []
        challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()

        # TODO: Need a generic challenge visibility call.
        # However, it should be stated that a solve on a gated challenge is not considered private.
        if challenge.state == 'hidden' and is_admin() is False:
            abort(404)

        Model = get_model()

        solves = Solves.query.join(Model, Solves.account_id == Model.id)\
            .filter(Solves.challenge_id == challenge_id, Model.banned == False, Model.hidden == False)\
            .order_by(Solves.date.asc())

        for solve in solves:
            response.append({
                'account_id': solve.account_id,
                'name': solve.account.name,
                'date': isoformat(solve.date)
            })

        return {
            'success': True,
            'data': response
        }
Beispiel #2
0
def submissions_listing(submission_type):
    filters = {}
    if submission_type:
        filters["type"] = submission_type

    curr_page = abs(int(request.args.get("page", 1, type=int)))
    results_per_page = 50
    page_start = results_per_page * (curr_page - 1)
    page_end = results_per_page * (curr_page - 1) + results_per_page
    sub_count = Submissions.query.filter_by(**filters).count()
    page_count = int(
        sub_count / results_per_page) + (sub_count % results_per_page > 0)

    Model = get_model()

    submissions = (Submissions.query.add_columns(
        Submissions.id,
        Submissions.type,
        Submissions.challenge_id,
        Submissions.provided,
        Submissions.account_id,
        Submissions.date,
        Challenges.name.label("challenge_name"),
        Model.name.label("team_name"),
    ).filter_by(**filters).join(Challenges).join(Model).order_by(
        Submissions.date.desc()).slice(page_start, page_end).all())

    return render_template(
        "admin/submissions.html",
        submissions=submissions,
        page_count=page_count,
        curr_page=curr_page,
        type=submission_type,
    )
    def solve(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:
        """
        chal = multiChallenge.query.filter_by(id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data["submission"].strip()

        Model = get_model()

        solve = Solves(
            user_id=user.id,
            team_id=team.id if team else None,
            challenge_id=challenge.id,
            ip=get_ip(req=request),
            provided=submission,
        )
        db.session.add(solve)

        solve_count = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            ).count())

        db.session.commit()
        db.session.close()
Beispiel #4
0
    def calculate_value(cls, challenge):
        Model = get_model()

        solve_count = (
            Solves.query.join(Model, Solves.account_id == Model.id)
            .filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            )
            .count()
        )

        # If the solve count is 0 we shouldn't manipulate the solve count to
        # let the math update back to normal
        if solve_count != 0:
            # We subtract -1 to allow the first solver to get max point value
            solve_count -= 1

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = (
            ((challenge.minimum - challenge.initial) / (challenge.decay ** 2))
            * (solve_count ** 2)
        ) + challenge.initial

        value = math.ceil(value)

        if value < challenge.minimum:
            value = challenge.minimum

        challenge.value = value
        db.session.commit()
        return challenge
Beispiel #5
0
    def recalculate_awards(cls, challenge):
        """
        Recalculate all of the awards after challenge has been edited or solves/users were removed
        You have to call db.session.commit() manually after this!
        """
        Model = get_model()

        solves = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id).order_by(Solves.id).all())

        i = 0
        for solve in solves:
            award = FirstBloodAward.query.filter(
                FirstBloodAward.solve_id == solve.id).first()

            if FirstBloodValueChallenge._can_get_award(challenge, solve):
                award_data = FirstBloodValueChallenge._gen_award_data(
                    challenge, solve, i + 1)
                i += 1
            else:
                award_data = None

            if award_data is not None:
                if award is not None:
                    for k, v in award_data.items():
                        setattr(award, k, v)
                else:
                    award = FirstBloodAward(**award_data)
                    db.session.add(award)
            else:
                if award:
                    db.session.delete(award)
Beispiel #6
0
    def update(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():
            setattr(challenge, attr, value)

        Model = get_model()

        solve_count = (
            Solves.query.join(Model, Solves.account_id == Model.id)
                .filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            )
                .count()
        )

        db.session.commit()
        return challenge
Beispiel #7
0
    def calculate_value(cls, challenge):
        Model = get_model()

        solve_count = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            ).count())

        if solve_count == 0:
            solve_count = 1

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = challenge.initial * (
            1.0 + math.log10(solve_count)**2 / 2.079)**(-1.5)

        value = math.ceil(value)

        if value < challenge.minimum:
            value = challenge.minimum

        challenge.value = value
        db.session.commit()
        return challenge
Beispiel #8
0
    def solve(cls, user, team, challenge, request):
        super().solve(user, team, challenge, request)

        # Get the Solve object that was just inserted by solve() (that method should really return it :<)
        solve = Solves.query.filter(Solves.challenge_id == challenge.id)
        if user is not None:
            solve = solve.filter(Solves.user_id == user.id)
        if team is not None:
            solve = solve.filter(Solves.team_id == team.id)
        solve = solve.first()

        if FirstBloodValueChallenge._can_get_award(challenge, solve):
            # Figure out the solve number
            Model = get_model()

            solve_count = (Solves.query.join(
                Model, Solves.account_id == Model.id).filter(
                    Solves.id <= solve.id,
                    Solves.challenge_id == challenge.id,
                    Model.hidden == False,
                    Model.banned == False,
                ).count())

            # Insert the award into the database
            award_data = FirstBloodValueChallenge._gen_award_data(
                challenge, solve, solve_count)
            if award_data is not None:
                award = FirstBloodAward(**award_data)
                db.session.add(award)
                db.session.commit()
Beispiel #9
0
    def solve(user, team, challenge, request):
        chal = DynICPCModel.query.filter_by(id=challenge.id).first()
        Model = get_model()
        solve = Solves(user_id=user.id,
                       team_id=team.id if team else None,
                       challenge_id=challenge.id,
                       ip=get_ip(request),
                       provided=request.judge_result['submission_id'])
        db.session.add(solve)
        solve_count = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id,
                not Model.hidden,
                not Model.banned,
            ).count())

        # We subtract -1 to allow the first solver to get max point value
        solve_count -= 1

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = (((chal.minimum - chal.initial) / (chal.decay**2)) *
                 (solve_count**2)) + chal.initial

        value = math.ceil(value)
        if value < chal.minimum:
            value = chal.minimum

        chal.value = value
        db.session.commit()
        db.session.close()
Beispiel #10
0
    def get(self, challenge_id):
        response = []
        challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()

        # TODO: Need a generic challenge visibility call.
        # However, it should be stated that a solve on a gated challenge is not considered private.
        if challenge.state == 'hidden' and is_admin() is False:
            abort(404)

        Model = get_model()

        solves = Solves.query.join(Model, Solves.account_id == Model.id)\
            .filter(Solves.challenge_id == challenge_id, Model.banned == False, Model.hidden == False)\
            .order_by(Solves.date.asc())

        endpoint = None
        if get_config('user_mode') == TEAMS_MODE:
            endpoint = 'teams.public'
            arg = 'team_id'
        elif get_config('user_mode') == USERS_MODE:
            endpoint = 'users.public'
            arg = 'user_id'

        for solve in solves:
            response.append({
                'account_id': solve.account_id,
                'name': solve.account.name,
                'date': isoformat(solve.date),
                'account_url': url_for(endpoint, **{arg: solve.account_id})
            })

        return {
            'success': True,
            'data': response
        }
Beispiel #11
0
        def wrapper(user, team, challenge, request):
            chal_solve_func(user, team, challenge, request)

            notifier = get_configured_notifier()
            if notifier and bool(get_config('notifier_send_solves')):
                if get_mode_as_word() == TEAMS_MODE:
                    solver = team
                    solver_url = url_for("teams.public", team_id=solver.account_id, _external=True)
                else:
                    solver = user
                    solver_url = url_for("users.public", user_id=solver.account_id, _external=True)
                challenge_url = url_for('challenges.listing', _external=True, _anchor='{challenge.name}-{challenge.id}'.format(challenge=challenge))

                Model = get_model()
                solve_count = (
                    db.session.query(
                        db.func.count(Solves.id)
                    )
                    .filter(Solves.challenge_id == challenge.id)
                    .join(Model, Solves.account_id == Model.id)
                    .filter(Model.banned == False, Model.hidden == False)
                    .scalar()
                )

                max_solves = get_config('notifier_solve_count')
                max_solves = int(max_solves) if max_solves is not None else None

                if max_solves is None or solve_count <= max_solves:
                    notifier.notify_solve(get_config('notifier_solve_msg', '{solver} solved {challenge} ({solve_num} solve)'), solver.name, solver_url, challenge.name, challenge_url, solve_count)
Beispiel #12
0
def _build_solves_query(extra_filters=(), admin_view=False):
    # This can return None (unauth) if visibility is set to public
    user = get_current_user()
    # We only set a condition for matching user solves if there is a user and
    # they have an account ID (user mode or in a team in teams mode)
    if user is not None and user.account_id is not None:
        user_solved_cond = Solves.account_id == user.account_id
    else:
        user_solved_cond = false()
    # We have to filter solves to exclude any made after the current freeze
    # time unless we're in an admin view as determined by the caller.
    freeze = get_config("freeze")
    if freeze and not admin_view:
        freeze_cond = Solves.date < unix_time_to_utc(freeze)
    else:
        freeze_cond = true()
    # Finally, we never count solves made by hidden or banned users/teams, even
    # if we are an admin. This is to match the challenge detail API.
    AccountModel = get_model()
    exclude_solves_cond = and_(
        AccountModel.banned == false(),
        AccountModel.hidden == false(),
    )
    # This query counts the number of solves per challenge, as well as the sum
    # of correct solves made by the current user per the condition above (which
    # should probably only be 0 or 1!)
    solves_q = (db.session.query(
        Solves.challenge_id,
        sa_func.count(Solves.challenge_id),
        sa_func.sum(cast(user_solved_cond, sa_types.Integer)),
    ).join(AccountModel).filter(*extra_filters, freeze_cond,
                                exclude_solves_cond).group_by(
                                    Solves.challenge_id))
    return solves_q
    def attempt(challenge, request):
        """
        This method is used to check whether a given input is right or wrong. It does not make any changes and should
        return a boolean for correctness and a string to be shown to the user. It is also in charge of parsing the
        user's input from the request itself.

        :param challenge: The Challenge object from the database
        :param request: The request the user submitted
        :return: (boolean, string)
        """
        chal = SteamChallengeModel.query.filter_by(id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data["submission"].strip()
        flags = Flags.query.filter_by(challenge_id=challenge.id).all()
        for flag in flags:
            if get_flag_class(flag.type).compare(flag, submission):
                Model = get_model()
                solve_count = (
                    Solves.query.join(Model, Solves.account_id == Model.id)
                    .filter(
                        Solves.challenge_id == challenge.id,
                        Model.hidden == False,
                        Model.banned == False,
                    )
                    .count()
                )
                if solve_count == 0:
                    return True, "Congratulations - you were first to solve this, check the Steam Keys page for your key!"
                return True, "Correct"
        return False, "Incorrect"
Beispiel #14
0
    def calculate_value(cls, challenge):
        Model = get_model()

        solve_count = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            ).count())

        # If the solve count is 0 we should manipulate the solve count to
        # let the math update back to normal
        if solve_count == 0:
            # Use 1 instead
            solve_count = 1

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = ((1 + (math.log10(solve_count)**2) / challenge.decrease)
                 **(-challenge.slope)) * challenge.initial

        value = math.ceil(value)

        challenge.value = value
        db.session.commit()
        return challenge
Beispiel #15
0
    def get(self):
        challenges = Challenges.query\
            .add_columns('id', 'name', 'state', 'max_attempts')\
            .order_by(Challenges.value).all()

        Model = get_model()

        teams_with_points = db.session.query(Solves.account_id) \
            .join(Model) \
            .filter(Model.banned == False, Model.hidden == False) \
            .group_by(Solves.account_id) \
            .count()

        percentage_data = []
        for challenge in challenges:
            solve_count = Solves.query.join(Model, Solves.account_id == Model.id) \
                .filter(Solves.challenge_id == challenge.id, Model.banned == False, Model.hidden == False) \
                .count()

            if teams_with_points > 0:
                percentage = (float(solve_count) / float(teams_with_points))
            else:
                percentage = 0.0

            percentage_data.append({
                'id': challenge.id,
                'name': challenge.name,
                'percentage': percentage,
            })

        response = sorted(percentage_data,
                          key=lambda x: x['percentage'],
                          reverse=True)
        return {'success': True, 'data': response}
Beispiel #16
0
def statistics():
    update_check()

    Model = get_model()

    teams_registered = Teams.query.count()
    users_registered = Users.query.count()

    wrong_count = (Fails.query.join(Model,
                                    Fails.account_id == Model.id).filter(
                                        Model.banned == False,
                                        Model.hidden == False).count())

    solve_count = (Solves.query.join(Model,
                                     Solves.account_id == Model.id).filter(
                                         Model.banned == False,
                                         Model.hidden == False).count())

    challenge_count = Challenges.query.count()

    ip_count = Tracking.query.with_entities(Tracking.ip).distinct().count()

    solves_sub = (db.session.query(
        Solves.challenge_id,
        db.func.count(Solves.challenge_id).label("solves_cnt")).join(
            Model, Solves.account_id == Model.id).filter(
                Model.banned == False, Model.hidden == False).group_by(
                    Solves.challenge_id).subquery())

    solves = (db.session.query(
        solves_sub.columns.challenge_id,
        solves_sub.columns.solves_cnt,
        Challenges.name,
    ).join(Challenges, solves_sub.columns.challenge_id == Challenges.id).all())

    solve_data = {}
    for chal, count, name in solves:
        solve_data[name] = count

    most_solved = None
    least_solved = None
    if len(solve_data):
        most_solved = max(solve_data, key=solve_data.get)
        least_solved = min(solve_data, key=solve_data.get)

    db.session.close()

    return render_template(
        "admin/statistics.html",
        user_count=users_registered,
        team_count=teams_registered,
        ip_count=ip_count,
        wrong_count=wrong_count,
        solve_count=solve_count,
        challenge_count=challenge_count,
        solve_data=solve_data,
        most_solved=most_solved,
        least_solved=least_solved,
    )
Beispiel #17
0
    def solve(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:
        """
        chal = DynamicChallenge.query.filter_by(id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data['submission'].strip()

        Model = get_model()

        solve_count = Solves.query \
            .join(Model, Solves.account_id == Model.id) \
            .filter(Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False) \
            .count()

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        # value = (
        #     (
        #         (chal.minimum - chal.initial) / (chal.decay**2)
        #     ) * (solve_count**2)
        # ) + chal.initial

        if (team and team.hidden == True) or (not team
                                              and user.hidden == True):
            pass
        else:

            value = chal.initial * 0.03 + (
                (chal.initial * 0.97) /
                (1 + (max(0, solve_count) / 4.92201)**3.206069))

            value = math.ceil(value)

            if value < chal.minimum:
                value = chal.minimum

            if chal.decay != 0 and solve_count >= chal.decay:
                value = chal.minimum

            chal.value = value

        solve = Solves(user_id=user.id,
                       team_id=team.id if team else None,
                       challenge_id=challenge.id,
                       ip=get_ip(req=request),
                       provided=submission)
        db.session.add(solve)
        db.session.commit()
        db.session.close()
Beispiel #18
0
def before_flush(session, flush_context, instances):
    Model = get_model()

    for instance in session.deleted:
        if isinstance(instance, Solves):
            # A solve has been deleted - delete any awards associated with this solve
            award_ids = FirstBloodAward.query.with_entities(
                FirstBloodAward.id).filter(
                    FirstBloodAward.solve_id == instance.id).subquery()
            rowcount = Awards.query.filter(
                Awards.id.in_(award_ids)).delete(synchronize_session='fetch')
            if rowcount > 0:
                # Mark the awards for this challenge for recalculation
                if not hasattr(session, 'requires_award_recalculation'):
                    session.requires_award_recalculation = set()
                session.requires_award_recalculation.add(
                    Challenges.query.get(instance.challenge_id))
        if isinstance(instance, Users):
            # A user has been deleted - mark all challenges where this user had awards for recalculation
            # NOTE: This doesn't seem to be used by CTFd - see after_bulk_delete
            for award in FirstBloodAward.query.join(
                    Users, FirstBloodAward.user_id == Users.id).filter(
                        Users.id == instance.id).all():
                if not hasattr(session, 'requires_award_recalculation'):
                    session.requires_award_recalculation = set()
                session.requires_award_recalculation.add(award.solve.challenge)
                session.delete(award)
        if isinstance(instance, Teams):
            # A team has been deleted - mark all challenges where this team had awards for recalculation
            for award in FirstBloodAward.query.join(
                    Teams, FirstBloodAward.team_id == Teams.id).filter(
                        Teams.id == instance.id).all():
                if not hasattr(session, 'requires_award_recalculation'):
                    session.requires_award_recalculation = set()
                session.requires_award_recalculation.add(award.solve.challenge)
                session.delete(award)

    for instance in session.dirty:
        if session.is_modified(instance):
            if isinstance(instance, Model):
                if get_history(instance,
                               "hidden").has_changes() or get_history(
                                   instance, "banned").has_changes():
                    # The user/team hidden state has changed - update awards on all challenges this user has solved
                    for solve in Solves.query.join(
                            Challenges,
                            Solves.challenge_id == Challenges.id).filter(
                                Solves.account_id == instance.id,
                                Challenges.type == "firstblood"):
                        if not hasattr(session,
                                       'requires_award_recalculation'):
                            session.requires_award_recalculation = set()
                        session.requires_award_recalculation.add(
                            solve.challenge)
    def solve(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:
        """
        challenge = GuessPenaltyChallenge.query.filter_by(
            id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data["submission"].strip()

        Model = get_model()

        solve = Solves(
            user_id=user.id,
            team_id=team.id if team else None,
            challenge_id=challenge.id,
            ip=get_ip(req=request),
            provided=submission,
        )

        # Issue penalty award
        fail_count = (Fails.query.join(Model,
                                       Fails.account_id == Model.id).filter(
                                           Fails.challenge_id == challenge.id,
                                           Model.hidden == False,
                                           Model.banned == False,
                                       ).count())

        value = (((challenge.minimum - challenge.initial) /
                  (challenge.decay**2)) * (fail_count**2)) + challenge.initial

        value = math.ceil(value)

        if value < challenge.minimum:
            value = challenge.minimum

        value = value - challenge.initial

        penalty = Awards(user_id=user.id,
                         team_id=team.id if team else None,
                         name="FAIL Penalty: %s" % challenge.name,
                         description="Penalty for incorrect attempts",
                         value=value,
                         category=challenge.category,
                         icon="")

        # Commit to database
        db.session.add(solve)
        db.session.add(penalty)
        db.session.commit()
Beispiel #20
0
    def _can_get_award(cls, challenge, solve):
        # No awards for hidden challenges
        if challenge.state != 'visible':
            return False

        # No awards for hidden users
        Model = get_model()
        solver = Model.query.filter_by(id=solve.account_id).first()
        if solver.hidden or solver.banned:
            return False

        return True
Beispiel #21
0
def submissions_listing(submission_type):
    filters_by = {}
    if submission_type:
        filters_by["type"] = submission_type
    filters = []

    q = request.args.get("q")
    field = request.args.get("field")
    page = abs(request.args.get("page", 1, type=int))

    filters = build_model_filters(
        model=Submissions,
        query=q,
        field=field,
        extra_columns={
            "challenge_name": Challenges.name,
            "account_id": Submissions.account_id,
        },
    )

    Model = get_model()

    submissions = (
        Submissions.query.filter_by(**filters_by)
        .filter(*filters)
        .join(Challenges)
        .join(Model)
        .order_by(Submissions.date.desc())
        .paginate(page=page, per_page=50)
    )

    args = dict(request.args)
    args.pop("page", 1)

    return render_template(
        "admin/submissions.html",
        submissions=submissions,
        prev_page=url_for(
            request.endpoint,
            submission_type=submission_type,
            page=submissions.prev_num,
            **args
        ),
        next_page=url_for(
            request.endpoint,
            submission_type=submission_type,
            page=submissions.next_num,
            **args
        ),
        type=submission_type,
        q=q,
        field=field,
    )
Beispiel #22
0
    def solve(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:
        """
        chal = AliyunChallenge.query.filter_by(id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data["submission"].strip()

        Model = get_model()

        solve = Solves(
            user_id=user.id,
            team_id=team.id if team else None,
            challenge_id=challenge.id,
            ip=get_ip(req=request),
            provided=submission,
        )
        db.session.add(solve)

        solve_count = (
            Solves.query.join(Model, Solves.account_id == Model.id)
                .filter(
                Solves.challenge_id == challenge.id,
                Model.hidden == False,
                Model.banned == False,
            )
                .count()
        )

        # We subtract -1 to allow the first solver to get max point value
        solve_count -= 1

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = (
                        ((chal.minimum - chal.initial) / (chal.decay ** 2)) * (solve_count ** 2)
                ) + chal.initial

        value = math.ceil(value)

        if value < chal.minimum:
            value = chal.minimum

        chal.value = value

        db.session.commit()
        db.session.close()
Beispiel #23
0
def _build_solves_query(extra_filters=(), admin_view=False):
    """Returns queries and data that that are used for showing an account's solves.
    It returns a tuple of
        - SQLAlchemy query with (challenge_id, solve_count_for_challenge_id)
        - Current user's solved challenge IDs
    """
    # This can return None (unauth) if visibility is set to public
    user = get_current_user()
    # We only set a condition for matching user solves if there is a user and
    # they have an account ID (user mode or in a team in teams mode)
    AccountModel = get_model()
    if user is not None and user.account_id is not None:
        user_solved_cond = Solves.account_id == user.account_id
    else:
        user_solved_cond = false()
    # We have to filter solves to exclude any made after the current freeze
    # time unless we're in an admin view as determined by the caller.
    freeze = get_config("freeze")
    if freeze and not admin_view:
        freeze_cond = Solves.date < unix_time_to_utc(freeze)
    else:
        freeze_cond = true()
    # Finally, we never count solves made by hidden or banned users/teams, even
    # if we are an admin. This is to match the challenge detail API.
    exclude_solves_cond = and_(
        AccountModel.banned == false(), AccountModel.hidden == false(),
    )
    # This query counts the number of solves per challenge, as well as the sum
    # of correct solves made by the current user per the condition above (which
    # should probably only be 0 or 1!)
    solves_q = (
        db.session.query(Solves.challenge_id, sa_func.count(Solves.challenge_id),)
        .join(AccountModel)
        .filter(*extra_filters, freeze_cond, exclude_solves_cond)
        .group_by(Solves.challenge_id)
    )
    # Also gather the user's solve items which can be different from above query
    # For example, even if we are a hidden user, we should see that we have solved a challenge
    # however as a hidden user we are not included in the count of the above query
    if admin_view:
        # If we're an admin we should show all challenges as solved to break through any requirements
        challenges = Challenges.query.all()
        solve_ids = {challenge.id for challenge in challenges}
    else:
        # If not an admin we calculate solves as normal
        solve_ids = (
            Solves.query.with_entities(Solves.challenge_id)
            .filter(user_solved_cond)
            .all()
        )
        solve_ids = {value for value, in solve_ids}
    return solves_q, solve_ids
Beispiel #24
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 #25
0
    def update(challenge, request):
        data = request.form or request.get_json()

        for attr, value in data.items():
            if attr in ("initial", "minimum", "decay"):
                value = float(value)
            if attr in [
                    'id', 'name', 'value', 'description', 'category', 'state',
                    'max_attempts', 'type', 'type_data', 'max_cpu_time',
                    'max_real_time', 'max_memory', 'max_process_number',
                    'max_output_size', 'max_stack'
            ]:
                setattr(challenge, attr, value)
        if challenge.problem_id != -1 and api.challenge_prepared(
                challenge.problem_id):
            try:
                api.update_problem(challenge.problem_id,
                                   limits={
                                       i: int(data[i])
                                       for i in [
                                           'max_cpu_time', 'max_real_time',
                                           'max_memory', 'max_process_number',
                                           'max_output_size', 'max_stack'
                                       ]
                                   })
            except AssertionError:
                log('programming', '[{date}] update problem error')

        Model = get_model()

        solve_count = (Solves.query.join(
            Model, Solves.account_id == Model.id).filter(
                Solves.challenge_id == challenge.id,
                not Model.hidden,
                not Model.banned,
            ).count())

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        value = (((challenge.minimum - challenge.initial) /
                  (challenge.decay**2)) * (solve_count**2)) + challenge.initial

        value = math.ceil(value)

        if value < challenge.minimum:
            value = challenge.minimum

        challenge.value = value

        db.session.commit()
        return challenge
Beispiel #26
0
def get_unmatched_standings(count=None, admin=False, fields=[]):
    """
    Get standings as a list of tuples containing account_id, name, and score e.g. [(account_id, team_name, score)].
    Ties are broken by who reached a given score first based on the solve ID. Two users can have the same score but one
    user will have a solve ID that is before the others. That user will be considered the tie-winner.
    Challenges & Awards with a value of zero are filtered out of the calculations to avoid incorrect tie breaks.
    """

    if fields is None:
        fields = []
    Model = get_model()
    team_ids = get_team_ids()
    sumscores = get_scores(admin)
    """
    Admins can see scores for all users but the public cannot see banned users.
    Filters out banned users.
    Properly resolves value ties by ID.
    Different databases treat time precision differently so resolve by the row ID instead.
    """
    if admin:
        standings_query = (db.session.query(
            Model.id.label("account_id"),
            Model.oauth_id.label("oauth_id"),
            Model.name.label("name"),
            Model.hidden,
            Model.banned,
            sumscores.columns.score,
            *fields,
        ).join(sumscores, Model.id == sumscores.columns.account_id).filter(
            Model.id.notin_(team_ids)).order_by(sumscores.columns.score.desc(),
                                                sumscores.columns.id))
    else:
        standings_query = (db.session.query(
            Model.id.label("account_id"),
            Model.oauth_id.label("oauth_id"),
            Model.name.label("name"),
            sumscores.columns.score,
            *fields,
        ).join(sumscores, Model.id == sumscores.columns.account_id).filter(
            Model.banned == False,
            Model.hidden == False).filter(Model.id.notin_(team_ids)).order_by(
                sumscores.columns.score.desc(), sumscores.columns.id))
    """
    Only select a certain amount of users if asked.
    """
    if count is None:
        standings = standings_query.all()
    else:
        standings = standings_query.limit(count).all()

    return standings
Beispiel #27
0
    def solve(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:
        """
        chal = ADAChallenge.query.filter_by(id=challenge.id).first()
        data = request.form or request.get_json()
        submission = data["submission"].strip()

        Model = get_model()
        if Model == Users:
            attacker = GlowwormContainers.query.filter_by(user_id=user.id, challenge_id=challenge.id).first()
            attacker_name = user.name
            victim = GlowwormContainers.query.filter_by(flag=submission).first()
            victim_name = Users.query.filter_by(id=victim.user_id).first()
            team_id = None
        else:
            attacker = GlowwormContainers.query.filter_by(user_id=team.id, challenge_id=challenge.id).first()
            attacker_name = team.name
            victim = GlowwormContainers.query.filter_by(flag=submission).first()
            victim_name = Teams.query.filter_by(id=victim.user_id).first()
            team_id = victim_name.team_id

        attack = GlowwormAttacks(
            attack_id = attacker.user_id,
            attack_name = attacker_name,
            victim_id = victim.user_id,
            victim_name = victim_name.name,
            docker_id = victim.docker_id,
            envname = victim.docker_id.split("_",1)[1],
            flag = submission,
            round = get_round()
        )
        attack_log = GlowwormAttackLog(
            user_id=user.id,
            team_id=team.id if team else None,
            victim_user_id=victim_name.id,
            victim_team_id=team_id,
            challenge_id=challenge.id,
            ip=get_ip(req=request),
            provided=submission,
        )
        db.session.add(attack)
        db.session.add(attack_log)

        db.session.commit()
        db.session.close()
Beispiel #28
0
def submissions_listing(submission_type):
    filters_by = {}
    if submission_type:
        filters_by["type"] = submission_type
    filters = []

    q = request.args.get("q")
    field = request.args.get("field")
    page = abs(request.args.get("page", 1, type=int))

    if q:
        submissions = []
        if Submissions.__mapper__.has_property(
                field):  # The field exists as an exposed column
            filters.append(getattr(Submissions, field).like("%{}%".format(q)))

    Model = get_model()

    submissions = (Submissions.query.add_columns(
        Submissions.id,
        Submissions.type,
        Submissions.challenge_id,
        Submissions.provided,
        Submissions.account_id,
        Submissions.date,
        Challenges.name.label("challenge_name"),
        Model.name.label("team_name"),
    ).filter_by(**filters_by).filter(
        *filters).join(Challenges).join(Model).order_by(
            Submissions.date.desc()).paginate(page=page, per_page=50))

    args = dict(request.args)
    args.pop("page", 1)

    return render_template(
        "admin/submissions.html",
        submissions=submissions,
        prev_page=url_for(request.endpoint,
                          submission_type=submission_type,
                          page=submissions.prev_num,
                          **args),
        next_page=url_for(request.endpoint,
                          submission_type=submission_type,
                          page=submissions.next_num,
                          **args),
        type=submission_type,
        q=q,
        field=field,
    )
Beispiel #29
0
    def update(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)

        Model = get_model()

        solve_count = Solves.query \
            .join(Model, Solves.account_id == Model.id) \
            .filter(Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False) \
            .count()

        # It is important that this calculation takes into account floats.
        # Hence this file uses from __future__ import division
        # value = (((challenge.minimum - challenge.initial) / (challenge.decay ** 2)) * (solve_count ** 2)) + challenge.initial

        # value = math.ceil(value)

        # if value < challenge.minimum:
        #     value = challenge.minimum

        value = challenge.initial * 0.03 + (  # designed for 20 team at most
            (challenge.initial * 0.97) /
            (1 + (max(0, solve_count - 1) / 4.92201)**3.206069))

        value = math.ceil(value)

        if value < challenge.minimum:
            value = challenge.minimum

        if challenge.decay != 0 and solve_count >= challenge.decay:
            value = challenge.minimum

        challenge.value = value

        db.session.commit()
        return challenge
def manual_list_to_grade():
    Model = get_model()

    submissions = (Pending.query.add_columns(
        Pending.id,
        Pending.type,
        Pending.challenge_id,
        Pending.provided,
        Pending.account_id,
        Pending.date,
        Challenges.name.label("challenge_name"),
        Model.name.label("team_name"),
    ).join(Challenges).join(Model).order_by(Pending.date.desc())).all()

    return render_template('submissions.html', submissions=submissions)