Esempio n. 1
0
def load(app):
    app.db.create_all()
    CHALLENGE_CLASSES["virtual_machine_challenges"] = VMChallenge
    register_plugin_assets_directory(
        app, base_path="/plugins/virtual_machine_challenges/assets/"
    )
    register_admin_plugin_menu_bar(title="Guacamole", route="/guac")

    @app.route("/vm_control/<challenge_id>")
    @authed_only
    def vm_control_start_status(challenge_id):
        challenge = VMChallengesModel.query.filter_by(id=challenge_id).first()
        return start_status_for_user(challenge,get_current_user())

    @app.route("/vm_control/<challenge_id>/end")
    @authed_only
    def vm_control_end(challenge_id):
        challenge = VMChallengesModel.query.filter_by(id=challenge_id).first()
        return end_for_user(challenge,get_current_user())

    @app.route('/guac')
    def login_to_guac():
        add_current_user_to_guac()
        return redirect('/guacamole')


    @app.route('/getusername')
    def getusername():
       return get_current_user().name
Esempio n. 2
0
def load(app):
    dir_path = os.path.dirname(os.path.realpath(__file__))

    db.create_all()

    register_plugin_assets_directory(
        app, base_path="/plugins/CTFd-pwn-college-plugin/assets/")

    CHALLENGE_CLASSES["docker"] = DockerChallenge

    FLAG_CLASSES["user"] = UserFlag

    ssh_key_template_path = os.path.join(dir_path, "assets", "ssh_key",
                                         "settings.html")
    override_template("settings.html", open(ssh_key_template_path).read())
    app.view_functions["views.settings"] = ssh_key_settings
    Forms.keys = {"SSHKeyForm": SSHKeyForm}

    scoreboard_template_path = os.path.join(dir_path, "assets", "scoreboard",
                                            "scoreboard.html")
    override_template("scoreboard.html", open(scoreboard_template_path).read())
    app.view_functions["scoreboard.listing"] = scoreboard_listing

    blueprint = Blueprint("pwncollege_api", __name__)
    api = Api(blueprint,
              version="v1",
              doc=current_app.config.get("SWAGGER_UI"))
    api.add_namespace(docker_namespace, "/docker")
    api.add_namespace(user_flag_namespace, "/user_flag")
    api.add_namespace(ssh_key_namespace, "/ssh_key")
    api.add_namespace(download_namespace, "/download")
    api.add_namespace(terminal_namespace, "/terminal")
    api.add_namespace(binary_ninja_namespace, "/binary_ninja")
    api.add_namespace(belts_namespace, "/belts")
    app.register_blueprint(blueprint, url_prefix="/pwncollege_api/v1")

    app.register_blueprint(download)

    app.register_blueprint(terminal)
    register_user_page_menu_bar("Terminal", "/terminal")

    app.register_blueprint(grades)
    register_user_page_menu_bar("Grades", "/grades")
    register_admin_plugin_menu_bar("Grades", "/grades/all")
Esempio n. 3
0
def test_register_admin_plugin_menu_bar():
    """
    Test that register_admin_plugin_menu_bar() properly inserts into HTML and get_admin_plugin_menu_bar()
    returns the proper list.
    """
    app = create_ctfd()
    with app.app_context():
        register_admin_plugin_menu_bar(title='test_admin_plugin_name', route='/test_plugin')

        client = login_as_user(app, name="admin", password="******")
        r = client.get('/admin/statistics')
        output = r.get_data(as_text=True)
        assert '/test_plugin' in output
        assert 'test_admin_plugin_name' in output

        menu_item = get_admin_plugin_menu_bar()[0]
        assert menu_item.title == 'test_admin_plugin_name'
        assert menu_item.route == '/test_plugin'
    destroy_ctfd(app)
def test_register_admin_plugin_menu_bar():
    """
    Test that register_admin_plugin_menu_bar() properly inserts into HTML and get_admin_plugin_menu_bar()
    returns the proper list.
    """
    app = create_ctfd()
    with app.app_context():
        register_admin_plugin_menu_bar(title="test_admin_plugin_name",
                                       route="/test_plugin")

        client = login_as_user(app, name="admin", password="******")
        r = client.get("/admin/statistics")
        output = r.get_data(as_text=True)
        assert "/test_plugin" in output
        assert "test_admin_plugin_name" in output

        menu_item = get_admin_plugin_menu_bar()[0]
        assert menu_item.title == "test_admin_plugin_name"
        assert menu_item.route == "http://localhost/test_plugin"
    destroy_ctfd(app)
def load(app):
    CTFd_API_v1.add_namespace(cases_namespace, '/acm_chall/cases')
    CTFd_API_v1.add_namespace(submissions_namespace, '/acm_chall/submissions')
    CTFd_API_v1.add_namespace(challenge_namespace, '/acm_chall/challenge')
    app.register_blueprint(
        views, url_prefix='/acm_chall'
    )

    app.db.create_all()
    CHALLENGE_CLASSES["icpc_dynamic"] = DynICPCChallenge
    register_plugin_assets_directory(
        app, base_path="/plugins/ctfd-acm-challenges/assets/"
    )
    register_admin_plugin_menu_bar(
        'ACM Challenges', '/acm_chall/admin/judge_queue'
    )
    register_user_page_menu_bar(
        'ACM Status', '/acm_chall/judge_queue'
    )

    def poll():
        try:
            id, lang, callback = running.get(timeout=1)
            with app.app_context():
                ExecutorBase.get_executor(
                    id, lang, callback
                ).run()
        except Empty:
            pass
        except KeyboardInterrupt:
            pass

    scheduler = APScheduler()
    scheduler.init_app(app)
    scheduler.start()
    scheduler.add_job(id='acm-executor', func=poll, trigger="interval", seconds=10)
Esempio n. 6
0
def load(app):
    # upgrade()
    plugin_name = __name__.split('.')[-1]
    app.db.create_all()
    if not DBConfig.get_config("setup"):
        setup_default_configs()
    CHALLENGE_CLASSES["dynamic_docker"] = DynamicValueDockerChallenge
    register_plugin_assets_directory(
        app, base_path="/plugins/" + plugin_name + "/assets/"
    )

    page_blueprint = Blueprint(
        "ctfd-whale",
        __name__,
        template_folder="templates",
        static_folder="assets",
        url_prefix="/plugins/ctfd-whale"
    )
    register_admin_plugin_menu_bar(
        'Whale', '/plugins/ctfd-whale/admin/settings'
    )
    CTFd_API_v1.add_namespace(admin_namespace, path="/plugins/ctfd-whale/admin")
    CTFd_API_v1.add_namespace(user_namespace, path="/plugins/ctfd-whale")

    @page_blueprint.route('/admin/settings', methods=['GET', 'POST'])
    @admins_only
    def admin_list_configs():
        if request.method == 'POST':
            data = request.form.to_dict()
            data.pop('nonce')
            DBConfig.set_all_configs(data)
            RedisUtils(app=current_app).init_redis_port_sets()
        session["nonce"] = generate_nonce()
        configs = DBConfig.get_all_configs()
        return render_template('whale_config.html', configs=configs)

    @page_blueprint.route("/admin/containers")
    @admins_only
    def admin_list_containers():
        result = AdminContainers.get()
        view_mode = request.args.get('mode', session.get('view_mode', 'card'))
        session['view_mode'] = view_mode
        return render_template("whale_containers.html",
                               plugin_name=plugin_name,
                               containers=result['data']['containers'],
                               pages=result['data']['pages'],
                               curr_page=abs(request.args.get("page", 1, type=int)),
                               curr_page_start=result['data']['page_start'])

    def auto_clean_container():
        with app.app_context():
            results = DBContainer.get_all_expired_container()
            for r in results:
                ControlUtil.try_remove_container(r.user_id)

            configs = DBConfig.get_all_configs()
            containers = DBContainer.get_all_alive_container()

            config = ''.join([c.frp_config for c in containers])

            try:
                # you can authorize a connection by setting
                # frp_url = http://user:pass@ip:port
                frp_addr = configs.get("frp_api_url")
                if not frp_addr:
                    frp_addr = f'http://{configs.get("frp_api_ip", "frpc")}:{configs.get("frp_api_port", "7400")}'
                    # backward compatibility
                common = configs.get("frp_config_template")
                if '[common]' in common:
                    output = common + config
                else:
                    remote = requests.get(f'{frp_addr.lstrip("/")}/api/config')
                    assert remote.status_code == 200
                    configs["frp_config_template"] = remote.text
                    output = remote.text + config
                assert requests.put(
                    f'{frp_addr.lstrip("/")}/api/config', output, timeout=5
                ).status_code == 200
                assert requests.get(
                    f'{frp_addr.lstrip("/")}/api/reload', timeout=5
                ).status_code == 200
            except (requests.RequestException, AssertionError):
                raise WhaleError(
                    'frpc request failed\n' +
                    'please check the frp related configs'
                )

    app.register_blueprint(page_blueprint)

    try:
        lock_file = open("/tmp/ctfd_whale.lock", "w")
        lock_fd = lock_file.fileno()
        fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)

        scheduler = APScheduler()
        scheduler.init_app(app)
        scheduler.start()
        scheduler.add_job(
            id='whale-auto-clean', func=auto_clean_container,
            trigger="interval", seconds=10
        )

        redis_util = RedisUtils(app=app)
        redis_util.init_redis_port_sets()

        print("[CTFd Whale]Started successfully")
    except IOError:
        pass
Esempio n. 7
0
def load(app):
    # upgrade()
    app.db.create_all()
    CHALLENGE_CLASSES["dynamic_docker"] = DynamicValueDockerChallenge
    register_plugin_assets_directory(app,
                                     base_path="/plugins/ctfd-whale/assets/")
    register_admin_plugin_menu_bar('Whale',
                                   '/plugins/ctfd-whale/admin/settings')

    page_blueprint = Blueprint("ctfd-whale",
                               __name__,
                               template_folder="templates",
                               static_folder="assets",
                               url_prefix="/plugins/ctfd-whale")

    @page_blueprint.route('/admin/settings', methods=['GET'])
    @admins_only
    def admin_list_configs():
        configs = DBUtils.get_all_configs()
        return render_template('config.html', configs=configs)

    @page_blueprint.route('/admin/settings', methods=['PATCH'])
    @admins_only
    def admin_save_configs():
        req = request.get_json()
        DBUtils.save_all_configs(req.items())
        redis_util = RedisUtils(app=app)
        redis_util.init_redis_port_sets()
        return json.dumps({'success': True})

    @page_blueprint.route("/admin/containers", methods=['GET'])
    @admins_only
    def admin_list_containers():
        configs = DBUtils.get_all_configs()
        page = abs(request.args.get("page", 1, type=int))
        results_per_page = 50
        page_start = results_per_page * (page - 1)
        page_end = results_per_page * (page - 1) + results_per_page

        count = DBUtils.get_all_alive_container_count()
        containers = DBUtils.get_all_alive_container_page(page_start, page_end)

        pages = int(count / results_per_page) + (count % results_per_page > 0)
        return render_template("containers.html",
                               containers=containers,
                               pages=pages,
                               curr_page=page,
                               curr_page_start=page_start,
                               configs=configs)

    @page_blueprint.route("/admin/containers", methods=['DELETE'])
    @admins_only
    def admin_delete_container():
        user_id = request.args.get('user_id')
        ControlUtil.remove_container(app, user_id)
        return json.dumps({'success': True})

    @page_blueprint.route("/admin/containers", methods=['PATCH'])
    @admins_only
    def admin_renew_container():
        user_id = request.args.get('user_id')
        challenge_id = request.args.get('challenge_id')
        DBUtils.renew_current_container(user_id=user_id,
                                        challenge_id=challenge_id)
        return json.dumps({'success': True})

    @app.route('/api/v1/container', methods=['POST'])
    @authed_only
    def add_container():
        user_id = current_user.get_current_user().id
        redis_util = RedisUtils(app=app, user_id=user_id)

        if not redis_util.acquire_lock():
            return json.dumps({'success': False, 'msg': REQUEST_TOO_FAST})

        if ControlUtil.frequency_limit():
            return json.dumps({'success': False, 'msg': REQUEST_INTERVAL})

        ControlUtil.remove_container(app, user_id)
        challenge_id = request.args.get('challenge_id')
        ControlUtil.check_challenge(challenge_id, user_id)

        configs = DBUtils.get_all_configs()
        current_count = DBUtils.get_all_alive_container_count()
        if int(configs.get("docker_max_container_count")) <= int(
                current_count):
            return json.dumps({'success': False, 'msg': TOO_MANY_INSTANCES})

        dynamic_docker_challenge = DynamicDockerChallenge.query \
            .filter(DynamicDockerChallenge.id == challenge_id) \
            .first_or_404()
        flag = "flag{" + str(uuid.uuid4()) + "}"
        if dynamic_docker_challenge.redirect_type == "http":
            ControlUtil.add_container(app=app,
                                      user_id=user_id,
                                      challenge_id=challenge_id,
                                      flag=flag)
        else:
            port = redis_util.get_available_port()
            ControlUtil.add_container(app=app,
                                      user_id=user_id,
                                      challenge_id=challenge_id,
                                      flag=flag,
                                      port=port)

        redis_util.release_lock()
        return json.dumps({'success': True})

    @app.route('/api/v1/container', methods=['GET'])
    @authed_only
    def list_container():
        user_id = current_user.get_current_user().id
        challenge_id = request.args.get('challenge_id')
        ControlUtil.check_challenge(challenge_id, user_id)
        data = ControlUtil.get_container(user_id=user_id)
        configs = DBUtils.get_all_configs()
        domain = configs.get('frp_http_domain_suffix', "")
        timeout = int(configs.get("docker_timeout", "3600"))
        if data is not None:
            if int(data.challenge_id) != int(challenge_id):
                return json.dumps({'success': False})
            dynamic_docker_challenge = DynamicDockerChallenge.query \
                .filter(DynamicDockerChallenge.id == data.challenge_id) \
                .first_or_404()
            lan_domain = hashlib.md5(
                (str(user_id) + "-" + data.uuid).encode()).hexdigest()
            if dynamic_docker_challenge.redirect_type == "http":
                if int(configs.get('frp_http_port', "80")) == 80:
                    return json.dumps({
                        'success':
                        True,
                        'type':
                        'http',
                        'domain':
                        lan_domain + domain,
                        'remaining_time':
                        timeout - (datetime.now() - data.start_time).seconds,
                        'lan_domain':
                        lan_domain
                    })
                else:
                    return json.dumps({
                        'success':
                        True,
                        'type':
                        'http',
                        'domain':
                        lan_domain + domain + ":" +
                        configs.get('frp_http_port', "80"),
                        'remaining_time':
                        timeout - (datetime.now() - data.start_time).seconds,
                        'lan_domain':
                        lan_domain
                    })
            else:
                return json.dumps({
                    'success':
                    True,
                    'type':
                    'redirect',
                    'ip':
                    configs.get('frp_direct_ip_address', ""),
                    'port':
                    data.port,
                    'remaining_time':
                    timeout - (datetime.now() - data.start_time).seconds,
                    'lan_domain':
                    lan_domain
                })
        else:
            return json.dumps({'success': True})

    @app.route('/api/v1/container', methods=['DELETE'])
    @authed_only
    def remove_container():
        user_id = current_user.get_current_user().id
        redis_util = RedisUtils(app=app, user_id=user_id)
        if not redis_util.acquire_lock():
            return json.dumps({'success': False, 'msg': REQUEST_TOO_FAST})

        if ControlUtil.frequency_limit():
            return json.dumps({'success': False, 'msg': REQUEST_INTERVAL})

        if ControlUtil.remove_container(app, user_id):
            redis_util.release_lock()

            return json.dumps({'success': True})
        else:
            return json.dumps({'success': False, 'msg': DESTROY_FAILED})

    @app.route('/api/v1/container', methods=['PATCH'])
    @authed_only
    def renew_container():
        user_id = current_user.get_current_user().id
        redis_util = RedisUtils(app=app, user_id=user_id)
        if not redis_util.acquire_lock():
            return json.dumps({'success': False, 'msg': REQUEST_TOO_FAST})

        if ControlUtil.frequency_limit():
            return json.dumps({'success': False, 'msg': REQUEST_INTERVAL})

        configs = DBUtils.get_all_configs()
        challenge_id = request.args.get('challenge_id')
        ControlUtil.check_challenge(challenge_id, user_id)
        docker_max_renew_count = int(configs.get("docker_max_renew_count"))
        container = ControlUtil.get_container(user_id)
        if container is None:
            return json.dumps({'success': False, 'msg': INSTANCE_NOT_FOUND})
        if container.renew_count >= docker_max_renew_count:
            return json.dumps({'success': False, 'msg': RENEW_EXCEEDED})
        ControlUtil.renew_container(user_id=user_id, challenge_id=challenge_id)
        redis_util.release_lock()
        return json.dumps({'success': True})

    def auto_clean_container():
        with app.app_context():
            results = DBUtils.get_all_expired_container()
            for r in results:
                ControlUtil.remove_container(app, r.user_id)

            FrpUtils.update_frp_redirect()

    app.register_blueprint(page_blueprint)

    try:
        lock_file = open("/tmp/ctfd_whale.lock", "w")
        lock_fd = lock_file.fileno()
        fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)

        scheduler = APScheduler()
        scheduler.init_app(app)
        scheduler.start()
        scheduler.add_job(id='whale-auto-clean',
                          func=auto_clean_container,
                          trigger="interval",
                          seconds=10)

        redis_util = RedisUtils(app=app)
        redis_util.init_redis_port_sets()

        print("[CTFd Whale]Started successfully")
    except IOError:
        pass
Esempio n. 8
0
def load(app):
    # upgrade()
    plugin_name = __name__.split('.')[-1]
    set_config('whale:plugin_name', plugin_name)
    app.db.create_all()
    if not get_config("whale:setup"):
        setup_default_configs()

    register_plugin_assets_directory(
        app,
        base_path=f"/plugins/{plugin_name}/assets",
        endpoint='plugins.ctfd-whale.assets')
    register_admin_plugin_menu_bar(title='Whale',
                                   route='/plugins/ctfd-whale/admin/settings')

    DynamicValueDockerChallenge.templates = {
        "create": f"/plugins/{plugin_name}/assets/create.html",
        "update": f"/plugins/{plugin_name}/assets/update.html",
        "view": f"/plugins/{plugin_name}/assets/view.html",
    }
    DynamicValueDockerChallenge.scripts = {
        "create": "/plugins/ctfd-whale/assets/create.js",
        "update": "/plugins/ctfd-whale/assets/update.js",
        "view": "/plugins/ctfd-whale/assets/view.js",
    }
    CHALLENGE_CLASSES["dynamic_docker"] = DynamicValueDockerChallenge

    page_blueprint = Blueprint("ctfd-whale",
                               __name__,
                               template_folder="templates",
                               static_folder="assets",
                               url_prefix="/plugins/ctfd-whale")
    CTFd_API_v1.add_namespace(admin_namespace,
                              path="/plugins/ctfd-whale/admin")
    CTFd_API_v1.add_namespace(user_namespace, path="/plugins/ctfd-whale")

    @page_blueprint.route('/admin/settings')
    @admins_only
    def admin_list_configs():
        errors = WhaleChecks.perform()
        if not errors and get_config("whale:refresh", "false"):
            DockerUtils.init()
            CacheProvider(app=current_app).init_port_sets()
            set_config("whale:refresh", "false")
        return render_template('whale_config.html', errors=errors)

    @page_blueprint.route("/admin/containers")
    @admins_only
    def admin_list_containers():
        result = AdminContainers.get()
        view_mode = request.args.get('mode', session.get('view_mode', 'list'))
        session['view_mode'] = view_mode
        return render_template("whale_containers.html",
                               plugin_name=plugin_name,
                               containers=result['data']['containers'],
                               pages=result['data']['pages'],
                               curr_page=abs(
                                   request.args.get("page", 1, type=int)),
                               curr_page_start=result['data']['page_start'])

    def auto_clean_container():
        with app.app_context():
            results = DBContainer.get_all_expired_container()
            containers = DBContainer.get_all_alive_container()

            config = ''.join([c.frp_config for c in containers])

            try:
                # you can authorize a connection by setting
                # frp_url = http://user:pass@ip:port
                frp_addr = get_config("whale:frp_api_url")
                if not frp_addr:
                    frp_addr = f'http://{get_config("whale:frp_api_ip", "frpc")}:{get_config("whale:frp_api_port", "7400")}'
                    # backward compatibility
                common = get_config("whale:frp_config_template", '')
                if '[common]' in common:
                    output = common + config
                else:
                    remote = requests.get(f'{frp_addr.rstrip("/")}/api/config')
                    assert remote.status_code == 200
                    set_config("whale:frp_config_template", remote.text)
                    output = remote.text + config
                assert requests.put(f'{frp_addr.rstrip("/")}/api/config',
                                    output,
                                    timeout=5).status_code == 200
                assert requests.get(f'{frp_addr.rstrip("/")}/api/reload',
                                    timeout=5).status_code == 200
            except (requests.RequestException, AssertionError) as e:
                raise WhaleError(
                    '\nfrpc request failed\n' + (f'{e}\n' if str(e) else '') +
                    'please check the frp related configs') from None

    app.register_blueprint(page_blueprint)

    try:
        CacheProvider(app=app).init_port_sets()
        DockerUtils.init()
    except Exception:
        warnings.warn("Initialization Failed. Please check your configs.",
                      WhaleWarning)

    try:
        lock_file = open("/tmp/ctfd_whale.lock", "w")
        lock_fd = lock_file.fileno()
        fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)

        scheduler = APScheduler()
        scheduler.init_app(app)
        scheduler.start()
        # scheduler.add_job(
        #     id='whale-auto-clean', func=auto_clean_container,
        #     trigger="interval", seconds=10
        # )

        print("[CTFd Whale] Started successfully")
    except IOError:
        pass