def reset_db(): remove_PBA_files() # We can't drop system collections. [mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')] ConfigService.init_config() logger.info('DB was reset') return jsonify(status='OK')
def add_system_info_ssh_keys_to_config(ssh_info): for user in ssh_info: ConfigService.creds_add_username(user["name"]) # Public key is useless without private key if user["public_key"] and user["private_key"]: ConfigService.ssh_add_keys(user["public_key"], user["private_key"], user["name"], user["ip"])
def add_system_info_ssh_keys_to_config(ssh_info): for user in ssh_info: ConfigService.creds_add_username(user['name']) # Public key is useless without private key if user['public_key'] and user['private_key']: ConfigService.ssh_add_keys(user['public_key'], user['private_key'], user['name'], user['ip'])
def post(self): config_json = json.loads(request.data) if 'reset' in config_json: ConfigService.reset_config() else: if not ConfigService.update_config(config_json, should_encrypt=True): abort(400) return self.get()
def init_app(mongo_url): app = Flask(__name__) api = flask_restful.Api(app) api.representations = {'application/json': output_json} app.config['MONGO_URI'] = mongo_url app.config['SECRET_KEY'] = str(uuid.getnode()) app.config['JWT_AUTH_URL_RULE'] = '/api/auth' app.config['JWT_EXPIRATION_DELTA'] = env.get_auth_expiration_time() init_jwt(app) mongo.init_app(app) with app.app_context(): database.init() ConfigService.init_config() # If running on AWS, this will initialize the instance data, which is used "later" in the execution of the island. RemoteRunAwsService.init() app.add_url_rule('/', 'serve_home', serve_home) app.add_url_rule('/<path:static_path>', 'serve_static_file', serve_static_file) api.add_resource(Root, '/api') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') api.add_resource(ClientRun, '/api/client-monkey', '/api/client-monkey/') api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/<string:monkey_guid>') api.add_resource(MonkeyConfiguration, '/api/configuration', '/api/configuration/') api.add_resource(IslandConfiguration, '/api/configuration/island', '/api/configuration/island/') api.add_resource(MonkeyDownload, '/api/monkey/download', '/api/monkey/download/', '/api/monkey/download/<string:path>') api.add_resource(NetMap, '/api/netmap', '/api/netmap/') api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') api.add_resource(Report, '/api/report', '/api/report/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') api.add_resource(PBAFileDownload, '/api/pba/download/<string:path>') api.add_resource( FileUpload, '/api/fileUpload/<string:file_type>', '/api/fileUpload/<string:file_type>?load=<string:filename>', '/api/fileUpload/<string:file_type>?restore=<string:filename>') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') api.add_resource(AttackTelem, '/api/attack/<string:technique>') return app
def init_app_services(app): init_jwt(app) mongo.init_app(app) with app.app_context(): database.init() ConfigService.init_config() # If running on AWS, this will initialize the instance data, which is used "later" in the execution of the island. RemoteRunAwsService.init()
def apply_to_monkey_config(): """ Applies ATT&CK matrix to the monkey configuration :return: """ attack_techniques = AttackConfig.get_technique_values() monkey_config = ConfigService.get_config(False, True, True) monkey_schema = ConfigService.get_config_schema() AttackConfig.set_arrays(attack_techniques, monkey_config, monkey_schema) AttackConfig.set_booleans(attack_techniques, monkey_config, monkey_schema) ConfigService.update_config(monkey_config, True)
def get_config_exploits(): exploits_config_value = ['exploits', 'general', 'exploiter_classes'] default_exploits = ConfigService.get_default_config(False) for namespace in exploits_config_value: default_exploits = default_exploits[namespace] exploits = ConfigService.get_config_value(exploits_config_value, True, True) if exploits == default_exploits: return ['default'] return [ReportService.EXPLOIT_DISPLAY_DICT[exploit] for exploit in exploits]
def reset_db(): logger.info("Resetting database") # We can't drop system collections. [ Database.drop_collection(x) for x in mongo.db.collection_names() if not x.startswith("system.") and not x == AttackMitigations.COLLECTION_NAME ] ConfigService.init_config() AttackConfig.reset_config() logger.info("DB was reset") return jsonify(status="OK")
def get_config_exploits(): exploits_config_value = EXPLOITER_CLASSES_PATH default_exploits = ConfigService.get_default_config(False) for namespace in exploits_config_value: default_exploits = default_exploits[namespace] exploits = ConfigService.get_config_value(exploits_config_value, True, True) if exploits == default_exploits: return ["default"] return [ ExploiterDescriptorEnum.get_by_class_name(exploit).display_name for exploit in exploits ]
def reset_db(): logger.info('Resetting database') remove_PBA_files() # We can't drop system collections. [ Database.drop_collection(x) for x in mongo.db.collection_names() if not x.startswith('system.') and not x == AttackMitigations.COLLECTION_NAME ] ConfigService.init_config() AttackConfig.reset_config() logger.info('DB was reset') return jsonify(status='OK')
def get(self, file_type): """ Sends file to filepond :param file_type: Type indicates which file to send, linux or windows :return: Returns file contents """ # Verify that file_name is indeed a file from config if file_type == LINUX_PBA_TYPE: filename = ConfigService.get_config_value( copy.deepcopy(PBA_LINUX_FILENAME_PATH)) else: filename = ConfigService.get_config_value( copy.deepcopy(PBA_WINDOWS_FILENAME_PATH)) return send_from_directory(ABS_UPLOAD_PATH, filename)
def upload_pba_file(request_, is_linux=True): """ Uploads PBA file to island's file system :param request_: Request object containing PBA file :param is_linux: Boolean indicating if this file is for windows or for linux :return: filename string """ filename = secure_filename(request_.files['filepond'].filename) file_path = ABS_UPLOAD_PATH.joinpath(filename).absolute() request_.files['filepond'].save(str(file_path)) ConfigService.set_config_value( (PBA_LINUX_FILENAME_PATH if is_linux else PBA_WINDOWS_FILENAME_PATH), filename) return filename
def get_config_exploits(): exploits_config_value = EXPLOITER_CLASSES_PATH default_exploits = ConfigService.get_default_config(False) for namespace in exploits_config_value: default_exploits = default_exploits[namespace] exploits = ConfigService.get_config_value(exploits_config_value, True, True) if exploits == default_exploits: return ['default'] return [ ReportService.EXPLOIT_DISPLAY_DICT[exploit] for exploit in exploits ]
def delete(self, file_type): """ Deletes file that has been deleted on the front end :param file_type: Type indicates which file was deleted, linux of windows :return: Empty response """ filename_path = (PBA_LINUX_FILENAME_PATH if file_type == "PBAlinux" else PBA_WINDOWS_FILENAME_PATH) filename = ConfigService.get_config_value(filename_path) if filename: PostBreachFilesService.remove_file(filename) ConfigService.set_config_value(filename_path, "") return {}
def init_app(mongo_url): app = Flask(__name__) api = flask_restful.Api(app) api.representations = {'application/json': output_json} app.config['MONGO_URI'] = mongo_url app.config['SECRET_KEY'] = str(uuid.getnode()) app.config['JWT_AUTH_URL_RULE'] = '/api/auth' app.config['JWT_EXPIRATION_DELTA'] = env.get_auth_expiration_time() init_jwt(app) mongo.init_app(app) with app.app_context(): database.init() ConfigService.init_config() app.add_url_rule('/', 'serve_home', serve_home) app.add_url_rule('/<path:static_path>', 'serve_static_file', serve_static_file) api.add_resource(Root, '/api') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') api.add_resource(ClientRun, '/api/client-monkey', '/api/client-monkey/') api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/<string:monkey_guid>') api.add_resource(MonkeyConfiguration, '/api/configuration', '/api/configuration/') api.add_resource(IslandConfiguration, '/api/configuration/island', '/api/configuration/island/') api.add_resource(MonkeyDownload, '/api/monkey/download', '/api/monkey/download/', '/api/monkey/download/<string:path>') api.add_resource(NetMap, '/api/netmap', '/api/netmap/') api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') api.add_resource(Report, '/api/report', '/api/report/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') return app
def add_system_info_creds_to_config(creds): for user in creds: ConfigService.creds_add_username(user) if 'password' in creds[user]: ConfigService.creds_add_password(creds[user]['password']) if 'lm_hash' in creds[user]: ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) if 'ntlm_hash' in creds[user]: ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
def add_system_info_creds_to_config(creds): for user in creds: ConfigService.creds_add_username(creds[user]["username"]) if "password" in creds[user] and creds[user]["password"]: ConfigService.creds_add_password(creds[user]["password"]) if "lm_hash" in creds[user] and creds[user]["lm_hash"]: ConfigService.creds_add_lm_hash(creds[user]["lm_hash"]) if "ntlm_hash" in creds[user] and creds[user]["ntlm_hash"]: ConfigService.creds_add_ntlm_hash(creds[user]["ntlm_hash"])
def upload_pba_file(file_storage: FileStorage, is_linux=True): """ Uploads PBA file to island's file system :param request_: Request object containing PBA file :param is_linux: Boolean indicating if this file is for windows or for linux :return: filename string """ filename = secure_filename(file_storage.filename) file_contents = file_storage.read() PostBreachFilesService.save_file(filename, file_contents) ConfigService.set_config_value( (PBA_LINUX_FILENAME_PATH if is_linux else PBA_WINDOWS_FILENAME_PATH), filename) return filename
def _on_finished_infection(): # Checking is_report_being_generated here, because we don't want to wait to generate a # report; rather, # we want to skip and reply. if not is_report_being_generated() and not ReportService.is_latest_report_exists(): safe_generate_reports() if ConfigService.is_test_telem_export_enabled() and not TestTelemStore.TELEMS_EXPORTED: TestTelemStore.export_telems()
def update_aws_auth_params(): """ Updates the AWS authentication parameters according to config :return: True if new params allow successful authentication. False otherwise """ access_key_id = ConfigService.get_config_value( ['cnc', 'aws_config', 'aws_access_key_id'], False, True) secret_access_key = ConfigService.get_config_value( ['cnc', 'aws_config', 'aws_secret_access_key'], False, True) if (access_key_id != AwsService.access_key_id) or ( secret_access_key != AwsService.secret_access_key): AwsService.set_auth_params(access_key_id, secret_access_key) RemoteRunAwsService.is_auth = AwsService.test_client() AwsService.set_region(RemoteRunAwsService.aws_instance.region) return RemoteRunAwsService.is_auth
def test_is_aws_keys_setup(tmp_path): # Mock default configuration ConfigService.init_default_config() mongo.db = MockObject() mongo.db.config = MockObject() ConfigService.encrypt_config(ConfigService.default_config) mongo.db.config.find_one = MagicMock(return_value=ConfigService.default_config) assert not is_aws_keys_setup() bogus_key_value = get_datastore_encryptor().encrypt("bogus_aws_key") dpath.util.set( ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value ) dpath.util.set( ConfigService.default_config, AWS_KEYS_PATH + ["aws_access_key_id"], bogus_key_value ) assert is_aws_keys_setup()
def test_is_aws_keys_setup(): # Mock default configuration ConfigService.init_default_config() mongo.db = MockObject() mongo.db.config = MockObject() ConfigService.encrypt_config(ConfigService.default_config) mongo.db.config.find_one = MagicMock( return_value=ConfigService.default_config) assert not is_aws_keys_setup() # Make sure noone changed config path and broke this function bogus_key_value = encryptor.encryptor.enc('bogus_aws_key') dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH + ['aws_secret_access_key'], bogus_key_value) dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH + ['aws_access_key_id'], bogus_key_value) assert is_aws_keys_setup()
def delete(self, file_type): """ Deletes file that has been deleted on the front end :param file_type: Type indicates which file was deleted, linux of windows :return: Empty response """ filename_path = PBA_LINUX_FILENAME_PATH if file_type == 'PBAlinux' else PBA_WINDOWS_FILENAME_PATH filename = ConfigService.get_config_value(filename_path) file_path = ABS_UPLOAD_PATH.joinpath(filename) try: if os.path.exists(file_path): os.remove(file_path) ConfigService.set_config_value(filename_path, '') except OSError as e: LOG.error( "Can't remove previously uploaded post breach files: %s" % e) return {}
def delete(self, file_type): """ Deletes file that has been deleted on the front end :param file_type: Type indicates which file was deleted, linux of windows :return: Empty response """ if self.is_pba_file_type_supported(file_type): return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY, mimetype="text/plain") filename_path = (PBA_LINUX_FILENAME_PATH if file_type == "PBAlinux" else PBA_WINDOWS_FILENAME_PATH) filename = ConfigService.get_config_value(filename_path) if filename: PostBreachFilesService.remove_file(filename) ConfigService.set_config_value(filename_path, "") return {}
def add_exploit_extracted_creds_to_config(telemetry_json): if 'credentials' in telemetry_json['data']['info']: creds = telemetry_json['data']['info']['credentials'] for user in creds: ConfigService.creds_add_username(creds[user]['username']) if 'password' in creds[user] and creds[user]['password']: ConfigService.creds_add_password(creds[user]['password']) if 'lm_hash' in creds[user] and creds[user]['lm_hash']: ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) if 'ntlm_hash' in creds[user] and creds[user]['ntlm_hash']: ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
def add_exploit_extracted_creds_to_config(telemetry_json): if "credentials" in telemetry_json["data"]["info"]: creds = telemetry_json["data"]["info"]["credentials"] for user in creds: ConfigService.creds_add_username(creds[user]["username"]) if "password" in creds[user] and creds[user]["password"]: ConfigService.creds_add_password(creds[user]["password"]) if "lm_hash" in creds[user] and creds[user]["lm_hash"]: ConfigService.creds_add_lm_hash(creds[user]["lm_hash"]) if "ntlm_hash" in creds[user] and creds[user]["ntlm_hash"]: ConfigService.creds_add_ntlm_hash(creds[user]["ntlm_hash"])
def get(self, file_type): """ Sends file to filepond :param file_type: Type indicates which file to send, linux or windows :return: Returns file contents """ if self.is_pba_file_type_supported(file_type): return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY, mimetype="text/plain") # Verify that file_name is indeed a file from config if file_type == LINUX_PBA_TYPE: filename = ConfigService.get_config_value( copy.deepcopy(PBA_LINUX_FILENAME_PATH)) else: filename = ConfigService.get_config_value( copy.deepcopy(PBA_WINDOWS_FILENAME_PATH)) return send_from_directory( PostBreachFilesService.get_custom_pba_directory(), filename)
def get(self, guid=None, **kw): NodeService.update_dead_monkeys() # refresh monkeys status if not guid: guid = request.args.get('guid') if guid: monkey_json = mongo.db.monkey.find_one_or_404({"guid": guid}) monkey_json['config'] = ConfigService.decrypt_flat_config(monkey_json['config']) return monkey_json return {}
def get_cross_segment_issues(): scans = mongo.db.telemetry.find({'telem_type': 'scan'}, {'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1}) cross_segment_issues = [] # For now the feature is limited to 1 group. subnet_groups = [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])] for subnet_group in subnet_groups: cross_segment_issues += ReportService.get_cross_segment_issues_per_subnet_group(scans, subnet_group) return cross_segment_issues