Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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