Example #1
0
    def _check_capabilities_internal_dynamic(cls, app_bundle : Bundle,
                                             operation_name):
        """Checks whether the app sandbox allows a particular operation
        at runtime"""
        sandbox_check = tool_named("sandbox_check")

        # Initialize sandbox
        success = app_utils.init_sandbox(app_bundle, logger)
        if not success:
            return True # Unsandboxed apps may do whatever they want.

        sandbox_ruleset = app_utils.get_sandbox_rules(app_bundle)
        response = subprocess.run([sandbox_check, operation_name], input=sandbox_ruleset)

        # Capability is...
        if response.returncode == 0: # allowed
            return True
        elif response.returncode == 1: # not allowed
            return False
        else: # Some kind of error occurred.
            raise RuntimeError("Capability could not be checked.")
Example #2
0
    def get_entitlements(cls, filepath, raw=False):
        """
        Extract entitlements from a target binary.

        :param filepath: filepath for binary for which to extract entitlements
        :param raw: Whether to return the raw bytes. If false, returns a dictionary. Else, returns bytes
        :return: Dictionary containing application entitlements. Returns empty dictionary
                 in case of errors.
        """
        jtool = tool_named("jtool")

        env = os.environ.copy()
        env['ARCH'] = 'x86_64'

        exit_code, results = jtool("--ent", filepath, env=env)
        if exit_code != 0:
            return dict()

        if raw:
            return results
        else:
            return plist.parse_resilient_bytes(results)
Example #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
Example #4
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
Example #5
0
"""The Plist module assists in Plist (propertly list) parsing. Even though
the `plistlib` module exists, it fails to parse some files that are malformed
and choke up the XML parser, while being accepted by macOS. This module
bridges the gap and attempts to accept all plists that are accepted by macOS."""

import os
import tempfile
import subprocess
import plistlib
from extern.tools import tool_named

SANITIZER = tool_named("plist_sanitizer")


class tdict(dict):
    """
    Typed dict extension.
    """
    def typed_get(self, key, type, default=None):
        """
        Return the value for `key` if key is in the dictionary and is of type `type`,
        else return `default`.
        """
        if key not in self:
            return default

        if not isinstance(self[key], type):
            return default

        return self[key]