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
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")
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)
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
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
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