Ejemplo n.º 1
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)
Ejemplo n.º 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():
             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)
Ejemplo n.º 3
0
def export_csv():
    table = request.args.get('table')

    # TODO: It might make sense to limit dumpable tables. Config could potentially leak sensitive information.
    model = get_class_by_tablename(table)
    if model is None:
        abort(404)

    output = six.StringIO()
    writer = csv.writer(output)

    header = [column.name for column in model.__mapper__.columns]
    writer.writerow(header)

    responses = model.query.all()

    for curr in responses:
        writer.writerow([getattr(curr, column.name) for column in model.__mapper__.columns])

    output.seek(0)
    return send_file(
        output,
        as_attachment=True,
        cache_timeout=-1,
        attachment_filename="{name}-{table}.csv".format(name=ctf_config.ctf_name(), table=table)
    )
Ejemplo n.º 4
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)
    )
Ejemplo n.º 5
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()))

    if 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))
Ejemplo n.º 6
0
def export_ctf():
    backup = export_ctf_util()
    ctf_name = ctf_config.ctf_name()
    day = datetime.datetime.now().strftime("%Y-%m-%d")
    full_name = u"{}.{}.zip".format(ctf_name, day)
    return send_file(
        backup, cache_timeout=-1, as_attachment=True, attachment_filename=full_name
    )
Ejemplo n.º 7
0
def export_csv():
    table = request.args.get("table")

    output = dump_csv(name=table)

    return send_file(
        output,
        as_attachment=True,
        cache_timeout=-1,
        attachment_filename="{name}-{table}.csv".format(
            name=ctf_config.ctf_name(), table=table),
    )
Ejemplo n.º 8
0
def export_ctf(path=None):
    with app.app_context():
        backup = export_ctf_util()

        if path:
            with open(path, "wb") as target:
                shutil.copyfileobj(backup, target)
        else:
            name = ctf_name()
            day = datetime.datetime.now().strftime("%Y-%m-%d_%T")
            full_name = f"{name}.{day}.zip"

            with open(full_name, "wb") as target:
                shutil.copyfileobj(backup, target)

            print(f"Exported {full_name}")
Ejemplo n.º 9
0
def export_csv():
    table = request.args.get("table")

    # TODO: It might make sense to limit dumpable tables. Config could potentially leak sensitive information.
    model = get_class_by_tablename(table)
    if model is None:
        abort(404)

    temp = StringIO()
    writer = csv.writer(temp)

    header = [column.name for column in model.__mapper__.columns]
    writer.writerow(header)

    responses = model.query.all()

    for curr in responses:
        writer.writerow(
            [getattr(curr, column.name) for column in model.__mapper__.columns]
        )

    temp.seek(0)

    # In Python 3 send_file requires bytes
    output = BytesIO()
    output.write(temp.getvalue().encode("utf-8"))
    output.seek(0)
    temp.close()

    return send_file(
        output,
        as_attachment=True,
        cache_timeout=-1,
        attachment_filename="{name}-{table}.csv".format(
            name=ctf_config.ctf_name(), table=table
        ),
    )
Ejemplo n.º 10
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
        if current_user.get_wrong_submissions_per_time(
                get_current_user(), config.get_attempt_limit_type(),
                config.get_attempt_limit_hour(),
                config.get_attempt_limit_minute(),
                config.get_attempt_limit_second()
        ) > config.get_attempt_limit_count():
            if ctftime():
                chal_class.fail(user=user,
                                team=team,
                                challenge=challenge,
                                request=request)
            log('submissions',
                "[{date}] {name} submitted {submission} with kpm {kpm} [TOO FAST]",
                submission=request_data['submission'].encode('utf-8'),
                kpm=current_user.get_wrong_submissions_per_time(
                    get_current_user(), config.get_attempt_limit_type(),
                    config.get_attempt_limit_hour(),
                    config.get_attempt_limit_minute(),
                    config.get_attempt_limit_second()))
            # 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} with kpm {kpm} [CORRECT]",
                    submission=request_data['submission'].encode('utf-8'),
                    kpm=current_user.get_wrong_submissions_per_minute(
                        session['id']))
                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} with kpm {kpm} [WRONG]",
                    submission=request_data['submission'].encode('utf-8'),
                    kpm=current_user.get_wrong_submissions_per_minute(
                        session['id']))

                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} with kpm {kpm} [ALREADY SOLVED]",
                submission=request_data['submission'].encode('utf-8'),
                kpm=current_user.get_wrong_submissions_per_minute(
                    user.account_id))
            return {
                'success': True,
                'data': {
                    'status': "already_solved",
                    'message': 'You already solved this'
                }
            }
Ejemplo n.º 11
0
from CTFd import create_app
from CTFd.utils import config
from CTFd.utils.exports import export_ctf

import datetime
import sys
import shutil

app = create_app()
with app.app_context():
    print(
        "This file will be deleted in CTFd v4.0. Switch to using `python manage.py export_ctf`"
    )
    backup = export_ctf()

    if len(sys.argv) > 1:
        with open(sys.argv[1], "wb") as target:
            shutil.copyfileobj(backup, target)
    else:
        ctf_name = config.ctf_name()
        day = datetime.datetime.now().strftime("%Y-%m-%d")
        full_name = "{}.{}.zip".format(ctf_name, day)

        with open(full_name, "wb") as target:
            shutil.copyfileobj(backup, target)

        print("Exported {filename}".format(filename=full_name))
Ejemplo n.º 12
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 = {solve_id for solve_id, in solve_ids}
            # 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()
            }
            prereqs = set(requirements).intersection(all_challenge_ids)
            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]",
                name=user.name,
                submission=request_data.get("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():

                    # send discord webhook
                    # @TODO replace static url with a global variable containing the site url in config.py
                    user_url = "https://ctf.sigpwny.com/users/" + str(user.id)
                    challenge_url = "https://ctf.sigpwny.com/challenges#" + quote(
                        challenge.name)

                    description = ":white_check_mark: [{0}]({1}) solved [{2}]({3}) ({4})".format(
                        user.name, user_url, challenge.name, challenge_url,
                        challenge.value)

                    embeds = [{
                        "description":
                        description,
                        "color":
                        10553667,
                        "timestamp":
                        datetime.datetime.utcnow().strftime(
                            '%Y-%m-%d %H:%M:%SZ')
                    }]

                    send_discord_webhook(embeds)

                    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]",
                    name=user.name,
                    submission=request_data.get("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]",
                    name=user.name,
                    submission=request_data.get("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]",
                name=user.name,
                submission=request_data.get("submission", "").encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            return {
                "success": True,
                "data": {
                    "status": "already_solved",
                    "message": "You already solved this",
                },
            }
Ejemplo n.º 13
0
    def flaganizer_submit():
        if authed() is False:
            return {
                "success": True,
                "data": {
                    "status": "authentication_required"
                }
            }, 403

        if request.content_type != "application/json":
            request_data = request.form
        else:
            request_data = request.get_json()

        if ctf_paused():
            return (
                {
                    "success": True,
                    "data": {
                        "status": "paused",
                        "message": "{} is paused".format(config.ctf_name()),
                    },
                },
                403,
            )

        user = get_current_user()
        team = get_current_team()

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

        kpm = current_user.get_wrong_submissions_per_minute(user.account_id)

        frsp = requests.post(FLAGANIZER_VERIFY_ENDPOINT,
                             data={
                                 "flag": request_data.get("submission", "")
                             },
                             headers={
                                 "X-CTFProxy-SubAcc-JWT":
                                 request.headers.get("X-CTFProxy-JWT")
                             }).json()
        if frsp["Success"] == 0:
            if ctftime() or current_user.is_admin():
                placeholder_challenge = Challenges.query.filter_by(
                    name="wrong submission").first()
                if placeholder_challenge is None:
                    placeholder_challenge = Challenges(
                        name="wrong submission",
                        description=FLAGANIZER_DESCRIPTION_PREFIX +
                        "a placeholder challenge for unrecognized flags",
                        value=0,
                        category="misc",
                        state="hidden",
                        max_attempts=0)
                    db.session.add(placeholder_challenge)
                    db.session.commit()
                    db.session.close()
                    placeholder_challenge = Challenges.query.filter_by(
                        name="wrong submission").first()
                chal_class = get_chal_class(placeholder_challenge.type)
                if placeholder_challenge is not None:
                    chal_class.fail(user=user,
                                    team=team,
                                    challenge=placeholder_challenge,
                                    request=request)
                    clear_standings()
            log(
                "submissions",
                "[{date}] {name} submitted {submission} via flaganizer with kpm {kpm} [WRONG]",
                submission=request_data.get("submission", "").encode("utf-8"),
                kpm=kpm,
            )
            return {
                "success": True,
                "data": {
                    "status": "incorrect",
                    "message": frsp["Message"]
                },
            }

        challenge = Challenges.query.filter_by(
            description=FLAGANIZER_DESCRIPTION_PREFIX +
            frsp["Flag"]["Id"]).first()
        if challenge is None:
            challenge = Challenges(name=frsp["Flag"]["DisplayName"],
                                   description=FLAGANIZER_DESCRIPTION_PREFIX +
                                   frsp["Flag"]["Id"],
                                   value=frsp["Flag"]["Points"],
                                   category=frsp["Flag"]["Category"],
                                   state="hidden",
                                   max_attempts=0)

            db.session.add(challenge)
            db.session.commit()
        challenge_id = challenge.id

        if challenge.state == "locked":
            db.session.close()
            abort(403)

        if challenge.requirements:
            requirements = challenge.requirements.get("prerequisites", [])
            solve_ids = (Solves.query.with_entities(
                Solves.challenge_id).filter_by(
                    account_id=user.account_id).order_by(
                        Solves.challenge_id.asc()).all())
            solve_ids = set([solve_id for solve_id, in solve_ids])
            prereqs = set(requirements)
            if solve_ids >= prereqs:
                pass
            else:
                db.session.close()
                abort(403)

        chal_class = get_chal_class(challenge.type)

        if kpm > 10:
            if ctftime():
                chal_class.fail(user=user,
                                team=team,
                                challenge=challenge,
                                request=request)
            log(
                "submissions",
                "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [TOO FAST]",
                submission=request_data.get("submission", "").encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            # Submitting too fast
            db.session.close()
            return (
                {
                    "success": True,
                    "data": {
                        "status": "ratelimited",
                        "message":
                        "You're submitting flags too fast. Slow down.",
                    },
                },
                429,
            )

        solves = Solves.query.filter_by(account_id=user.account_id,
                                        challenge_id=challenge_id).first()

        # Challenge not solved yet
        if not solves:
            status, message = chal_class.attempt(challenge, request)
            chal_class.solve(user=user,
                             team=team,
                             challenge=challenge,
                             request=request)
            clear_standings()
            log(
                "submissions",
                "[{date}] {name} submitted {submission} on {challenge_id} via flaganizer with kpm {kpm} [CORRECT]",
                submission=request_data.get("submission", "").encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            db.session.close()
            return {
                "success": True,
                "data": {
                    "status": "correct",
                    "message": "Successfully submitted!"
                },
            }
        else:
            log(
                "submissions",
                "[{date}] {name} submitted {submission} on {challenge_id} via flaganizer with kpm {kpm} [ALREADY SOLVED]",
                submission=request_data.get("submission", "").encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            db.session.close()
            return {
                "success": True,
                "data": {
                    "status": "already_solved",
                    "message": "You already solved this",
                },
            }
Ejemplo n.º 14
0
    def post(self):
        if authed() is False:
            return {
                "success": True,
                "data": {
                    "status": "authentication_required"
                }
            }, 403

        if ctf_paused():
            return (
                {
                    "success": True,
                    "data": {
                        "status": "paused",
                        "message": "{} is paused".format(config.ctf_name()),
                    },
                },
                403,
            )

        if request.content_type != "application/json":
            request_data = request.form
        else:
            request_data = request.get_json()

        user = get_current_user()
        team = get_current_team()

        if config.is_teams_mode() and team is None:
            abort(403)

        # Anti-bruteforce / submitting Flags too quickly
        kpm = current_user.get_wrong_submissions_per_minute(user.account_id)
        if kpm > 10:
            log(
                "submissions",
                "[{date}] {name} submitted {submission} with kpm {kpm} [TOO FAST]",
                submission=request_data["submission"].encode("utf-8"),
                kpm=kpm,
            )
            # Submitting too fast
            return (
                {
                    "success": True,
                    "data": {
                        "status": "ratelimited",
                        "message":
                        "You're submitting flags too fast. Slow down.",
                    },
                },
                429,
            )

        challs = Challenges.query.all()
        for challenge in challs:
            correct, message = self.attempt_single(request, request_data, user,
                                                   team, kpm, challenge)
            if correct:
                return {
                    "success": True,
                    "data": {
                        "status": "correct",
                        "message": message
                    },
                }

        chal_class = get_chal_class('standard')
        challenge = Challenges.query.filter_by(
            name='__SECRET__').first_or_404()
        chal_class.fail(user=user,
                        team=team,
                        challenge=challenge,
                        request=request)

        return {
            "success": True,
            "data": {
                "status": "incorrect",
                "message": "Incorrect flag.",
            },
        }
Ejemplo n.º 15
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': '{} приостановлены'.format(config.ctf_name())
                }
            }, 403

        user = get_current_user()
        team = get_current_team()

        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
        if current_user.get_wrong_submissions_per_minute(session['id']) > 10:
            if ctftime():
                chal_class.fail(user=user,
                                team=team,
                                challenge=challenge,
                                request=request)
            log('submissions',
                "[{date}] {name} submitted {submission} with kpm {kpm} [TOO FAST]",
                submission=request_data['submission'].encode('utf-8'),
                kpm=current_user.get_wrong_submissions_per_minute(
                    session['id']))
            # Submitting too fast
            return {
                'success': True,
                'data': {
                    'status': "ratelimited",
                    'message':
                    "Вы отправляете флаги слишком быстро. Помедленнее."
                }
            }, 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': "У вас осталось 0 попыток"
                    }
                }, 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} with kpm {kpm} [CORRECT]",
                    submission=request_data['submission'].encode('utf-8'),
                    kpm=current_user.get_wrong_submissions_per_minute(
                        session['id']))
                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} with kpm {kpm} [WRONG]",
                    submission=request_data['submission'].encode('utf-8'),
                    kpm=current_user.get_wrong_submissions_per_minute(
                        session['id']))

                if max_tries:
                    # Off by one since fails has changed since it was gotten
                    attempts_left = max_tries - fails - 1
                    tries_str = 'попытки'
                    if attempts_left == 1:
                        tries_str = 'попытка'
                    # Add a punctuation mark if there isn't one
                    if message[-1] not in '!().;?[]{}':
                        message = message + '.'
                    return {
                        'success': True,
                        'data': {
                            'status':
                            "incorrect",
                            'message':
                            '{} У вас {} {} осталось.'.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} with kpm {kpm} [ALREADY SOLVED]",
                submission=request_data['submission'].encode('utf-8'),
                kpm=current_user.get_wrong_submissions_per_minute(
                    user.account_id))
            return {
                'success': True,
                'data': {
                    'status': "already_solved",
                    'message': 'Вы уже решили это'
                }
            }
Ejemplo n.º 16
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.get("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.get("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.get("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.get("submission", "").encode("utf-8"),
                challenge_id=challenge_id,
                kpm=kpm,
            )
            return {
                "success": True,
                "data": {
                    "status": "already_solved",
                    "message": "You already solved this",
                },
            }
Ejemplo n.º 17
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,
            )

        if current_user.already_rated(challenge_id):
            return (
                {
                    "success": False,
                    "data": {
                        "status": "rated",
                        "message": "user already rated",
                    }
                },
                403,
            )

        user = get_current_user()
        team = get_current_team()

        solves = Solves.query.filter_by(account_id=user.account_id,
                                        challenge_id=challenge_id).first()

        if not solves:
            return (
                {
                    "success": False,
                    "data": {
                        "status": "unsolved",
                        "message": "user not solved the challenge",
                    }
                },
                403,
            )
        else:
            chal_class = get_chal_class(challenge.type)
            chal_class.rate(user=user, challenge=challenge, request=request)