def extract_data(self, app: Bundle, result_path: str) -> bool: executable_path = app.executable_path() if not os.path.exists(executable_path): self.log_error("Executable for {} {} could not be found.".format( app.bundle_type, app.filepath)) return False fs.copy(executable_path, os.path.join(result_path, "executable.bin")) return True
def sandbox_status(app_bundle: Bundle, logger: logging.Logger) -> Optional[int]: process = subprocess.Popen([app_bundle.executable_path()], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Sandbox initialisation should be almost instant. If the application is still # running after a couple of seconds, the sandbox failed to initialise or is # not enabled at all. # We use 10 seconds as an arbitrary cutoff time. time.sleep(10) pid = str(process.pid) if process.poll() is not None: logger.error("Process terminated early: {}".format( app_bundle.executable_path())) return None sb_status = subprocess.run([tool_named("sandbox_status"), pid], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) process.kill() rx = re.compile(r'^Sandbox status for PID {} is (\d+)$'.format(pid)) m = rx.match(sb_status.stdout.decode().strip()) if m: return int(m.group(1)) logger.error( "`sandbox_status` did not return a status for executable at {}. Skipping." .format(app_bundle.executable_path())) return None
def _entitlements_can_be_parsed(app_bundle: Bundle) -> bool: """ Check whether an application's entitlements can be parsed by libsecinit. We only check part of the process, namely the parsing of entitlements via xpc_create_from_plist. :param app_bundle: Bundle for which to check whether the entitlements can be parsed :type app_bundle: Bundle :return: True, iff the entitlements of the main executable can be parsed, else false. """ # No entitlements, no problem # If the app contains no entitlements, entitlement validation cannot fail. if not app_bundle.has_entitlements(): return True exe_path = app_bundle.executable_path() raw_entitlements = Binary.get_entitlements(exe_path, raw=True) # Call the local xpc_vuln_checker program that does the actual checking. exit_code, _ = tool_named("xpc_vuln_checker")(input=raw_entitlements) return exit_code != 1
def init_sandbox(app_bundle: Bundle, logger: logging.Logger, force_initialisation: bool = False) -> bool: """ Initialises the sandbox for a particular app bundle. :param app_bundle: The App for which to initialise the App Sandbox :param logger: Logger object used to record failure cases :param force_initialisation: Whether to overwrite / start initialisation even if metadata exists that indicates the sandbox has already been initialised :return: Boolean value indicating whether the sandbox was successfully initialised (or was already initialised) """ # Guarding against a few applications that ship with entitlements libsecinit cannot parse. if not _entitlements_can_be_parsed(app_bundle): return False # Super useful environment variable used by libsecinit. If this variable is set, the application # is terminated after its sandbox is initialised. init_sandbox_environ = { **os.environ, 'APP_SANDBOX_EXIT_AFTER_INIT': str(1) } app_container = container_for_app(app_bundle) if app_container is not None and not force_initialisation: if logger: logger.info( "Container directory already existed. Skipping sandbox initialisation." ) return True if logger: logger.info("Starting process {} to initialize sandbox.".format( app_bundle.executable_path())) process = subprocess.Popen([app_bundle.executable_path()], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=init_sandbox_environ) # Sandbox initialisation should be almost instant. If the application is still # running after a couple of seconds, the sandbox failed to initialise. # We use 10 seconds as an arbitrary cutoff time. try: process.wait(10) except subprocess.TimeoutExpired: process.kill() if logger: logger.error( "Sandbox was not initialised successfully for executable at {}. Skipping." .format(app_bundle.executable_path())) return False # Check that there now is an appropriate container if container_for_app(app_bundle) is None: if logger: logger.info("Sandbox initialisation for executable {} succeeded \ but no appropriate container metadata was created.".format( app_bundle.executable_path())) return False return True