MacOS Agent Installer Flaw Lets Any Local User Become Root: A Deep Dive into Insecure Permission Handling + Video

Listen to this Post

Featured Image

Introduction:

In a recent private bug bounty discovery, a critical local privilege escalation (LPE) vulnerability was unearthed in a macOS agent application. The flaw stemmed not from complex memory corruption, but from fundamental failures in secure coding during the installation process. By leveraging world-writable directories and configuration files created by a root-privileged installer, any unprivileged local user on a shared system can manipulate trusted services, effectively hijacking the agent’s functionality and escalating their privileges to root. This article provides a technical walkthrough of the discovery process, the exploitation mechanics, and the hardening measures required to prevent such supply-chain-style privilege escalations.

Learning Objectives:

  • Understand how to set up a macOS virtualization lab for security research using UTM.
  • Learn to analyze macOS installer packages (PKG) and extract post-installation scripts.
  • Master the identification and exploitation of insecure file system permissions (777/666) leading to root privilege escalation.

You Should Know:

1. Setting Up the macOS Security Lab

To replicate this research, you need a safe, isolated environment to analyze potentially malicious or vulnerable software. Since physical Mac hardware isn’t always available, virtualization is key.

Step‑by‑step guide:

  • Install UTM: Download and install UTM (https://mac.getutm.app/). It is a free and powerful virtualization tool for Apple Silicon and Intel Macs.
  • Acquire macOS IPSW/Source: Download a macOS installer from the App Store or obtain the IPSW file for the target version.
  • Create the VM: In UTM, select “Virtualize,” then “macOS.” Choose the downloaded IPSW file. Allocate sufficient RAM (8GB+) and disk space (60GB+) for analysis.
  • Install and Snapshot: Complete the macOS installation. Immediately after a clean install, create a VM snapshot. This allows you to revert to a “clean state” after installing and testing the vulnerable agent, ensuring repeatability.

2. Extracting and Inspecting the macOS Installer Package

The vulnerability was found within the installer logic itself. macOS PKG files are essentially archives that contain installation scripts and payloads.

Step‑by‑step guide:

  • Locate the Installer: Download the target macOS agent (often a `.pkg` or `.dmg` file).
  • Expand the Package: Use the command line to expand the package without installing it. This reveals the `PackageInfo` and scripts.
    pkgutil --expand /path/to/agent.pkg /path/to/expanded/
    
  • Navigate to Scripts: Inside the expanded directory, look for a folder named `Scripts` or examine the `Distribution` file to find references to preinstall or postinstall scripts.
    cd /path/to/expanted/
    ls -la
    cat Distribution | grep -i script
    
  • Extract the Payload: If the script analysis requires seeing the actual file structure installed, extract the Payload file (a gzipped cpio archive):
    cd /path/to/expanded/
    cat Payload | gunzip -dc | cpio -i
    

3. Analyzing the `postinstall` Script for Insecure Permissions

The core issue lies in how the `postinstall` script configures the file system after the binary files are copied. This script runs with root privileges.

Step‑by‑step guide:

  • Locate the Script: In the expanded package directory, find the `postinstall` script (usually inside a `Scripts` directory).
  • Review the Code: Open the script in a text editor or use cat/less to view its contents. Look for dangerous permission commands:
    cat Scripts/postinstall
    
  • Identify Dangerous Patterns: The researcher found specific commands. Look for:
    – `mkdir -m 777` : Creates a directory with full read/write/execute permissions for everyone.
    – `chmod -R 777` : Recursively sets world-writable permissions on a directory tree.
    – `chmod 666` : Makes configuration files world-writable (but not executable).
  • Commands that change ownership to root, but then open permissions to “other.”
  • Example of Vulnerable Code:
    Vulnerable snippet found in postinstall
    mkdir -p /usr/local/test/agent/db
    chmod 777 /usr/local/test/agent/db
    touch /usr/local/test/agent/etc/performance.conf
    chmod 666 /usr/local/test/agent/etc/performance.conf
    chown -R root:wheel /usr/local/test/agent
    

4. Reproducing the Vulnerability (Verification)

After installing the package, verification is critical to confirm the misconfiguration.

Step‑by‑step guide:

  • Install the Agent: Run the installer (or use the `installer` command in the terminal for automation).
    sudo installer -pkg /path/to/agent.pkg -target /
    
  • Verify Permissions: Switch to a standard user account (or remain in your current non-root session) and run the following commands to check the state of the directories and files identified in the script.
    ls -ld /usr/local/test/agent/db
    ls -l /usr/local/test/agent/etc/performance.conf
    
  • Expected Output (Vulnerable):

`drwxrwxrwx` for the directory (777).

`-rw-rw-rw-` for the config file (666).

  • Test Write Access: As a low-privilege user, attempt to write to these locations.
    Attempt to create a file in the world-writable DB directory
    touch /usr/local/test/agent/db/test_by_attacker.txt
    
    Attempt to modify the world-writable config file
    echo "malicious=setting" >> /usr/local/test/agent/etc/performance.conf
    

  1. Exploitation: From Unprivileged User to Root Service Hijack
    The impact is that services (LaunchDaemons) running as root read these writable locations.

Step‑by‑step guide (Conceptual/PoC):

  • Identify the Root Service: Find the associated LaunchDaemon property list file (usually in /Library/LaunchDaemons/) that runs the agent. The `postinstall` script often installs and loads this.
    grep -r "test.agent" /Library/LaunchDaemons/
    cat /Library/LaunchDaemons/com.test.agent.plist
    

    Look for the `ProgramArguments` or `Program` key to find the executable path.

  • Analyze the Root Process: Determine what files this root process reads. In the researcher’s case, it likely reads the `performance.conf` file and writes logs/database entries to the world-writable `db` directory.
  • Execute the Attack:
  • Scenario A (Config Hijack): A local user modifies the world-writable performance.conf. They inject a directive that forces the root agent to execute an external command or load a malicious library.
    Example: If the agent reads config for a logging path, change it to a malicious script.
    echo "log_plugin=/tmp/malicious.sh" >> /usr/local/test/agent/etc/performance.conf
    
  • Scenario B (Path Hijack/Symlink): Since the `/usr/local/test/agent/db` directory is 777, a user could delete a file the root process expects to create and replace it with a symlink pointing to a sensitive system file (e.g., /etc/sudoers). When the root process writes to that file, it will follow the symlink and corrupt the target.
    cd /usr/local/test/agent/db
    rm -rf important.db
    ln -s /etc/sudoers important.db
    When the root service writes to important.db, it actually appends to /etc/sudoers.
    

6. Mitigation and Hardening Techniques

Developers must adhere to the principle of least privilege, especially during installation.

Step‑by‑step guide (Fix):