Skip to content

Homebrew macOS installer package trusts user-controlled `/var/tmp` plist and can assign Homebrew ownership to a local attacker

Low
MikeMcQuaid published GHSA-59v8-x8q4-px5c Jun 10, 2026

Package

Homebrew/brew macOS installer package

Affected versions

Homebrew 5.1.15-106-gaf69e65

Patched versions

6.0.0

Description

Summary

The Homebrew macOS installer package scripts trust /var/tmp/.homebrew_pkg_user.plist as an MDM install-user override without verifying ownership, permissions, or provenance. A local unprivileged user can pre-create that predictable file with their username before an administrator installs the official Homebrew package. During package installation, the privileged postinstall script uses that attacker-controlled username in recursive chown operations, making the attacker the owner of the Homebrew installation tree and cache paths.

This crosses a local privilege boundary because a low-privileged local user can influence root-run package scripts during a later administrator-driven installation. The direct, reproduced impact is attacker-controlled ownership of the installed Homebrew tree. That can let the attacker modify brew-managed files and potentially influence future commands run by administrative users who trust the installed Homebrew toolchain.

Details

Affected component: Homebrew/brew macOS installer package scripts shipped in the repository and used by the release workflow to build Homebrew-<version>.pkg.

Relevant source references in commit af69e652683b9a0299e8334d820bebc6487eb7d0:

  • Library/Homebrew/utils/macos_user.sh:30-45 defines homebrew-package-user().
  • Library/Homebrew/utils/macos_user.sh:32 defaults the override path to /var/tmp/.homebrew_pkg_user.plist.
  • Library/Homebrew/utils/macos_user.sh:33-40 accepts any readable plist value returned by defaults read ... HOMEBREW_PKG_USER when the value is non-empty. It does not check owner, mode, symlink state, ACLs, directory safety, or whether the file was created by a trusted MDM/root context.
  • package/scripts/preinstall:16-22 treats homebrew-package-user success as enough to proceed.
  • package/scripts/postinstall:86-92 resolves homebrew_pkg_user with homebrew-package-user while the package script runs with installer privileges.
  • package/scripts/postinstall:94-103 uses that value in ownership changes, including chown -R "${homebrew_pkg_user}:admin" . for /opt/homebrew installs and recursive ownership changes for /usr/local/Homebrew installs.
  • package/scripts/postinstall:106-115 uses the same user to derive a home directory and then chown -R "${homebrew_pkg_user}:staff" "${user_cache_dir}".
  • .github/workflows/release.yml:136-145 copies preinstall, postinstall, and macos_user.sh into the installer script directory.
  • .github/workflows/release.yml:147-161 builds the signed component package with pkgbuild --scripts "${HOMEBREW_INSTALLER_SCRIPTS}" --identifier sh.brew.homebrew --install-location /opt/homebrew.
  • .github/workflows/release.yml:167-174 wraps it into Homebrew-${HOMEBREW_VERSION}.pkg with productbuild.
  • package/Distribution.xml:3-19 identifies the package reference sh.brew.homebrew and Homebrew.pkg; package/Distribution.xml:29-30 supports macOS 14.0+.

Source-to-sink path:

local attacker writes /var/tmp/.homebrew_pkg_user.plist -> homebrew-package-user() reads HOMEBREW_PKG_USER from the predictable world-writable-directory path -> preinstall accepts the selected user -> root-run postinstall stores it in homebrew_pkg_user -> root-run chown recursively assigns the Homebrew installation tree and cache directory to that user.

Missing or bypassed mitigation:

  • The helper validates only that the plist exists and that HOMEBREW_PKG_USER is non-empty.
  • It does not require the file to be root-owned, MDM-owned, non-symlink, non-world-writable, or located in a trusted directory.
  • The default path is under /var/tmp, a location intended for local temporary files and commonly writable by unprivileged local users.
  • preinstall and postinstall do not revalidate the selected user before privileged chown sinks.

Default/common exposure evidence:

  • The default override path is compiled into the shipped helper: /var/tmp/.homebrew_pkg_user.plist at Library/Homebrew/utils/macos_user.sh:32.
  • The release workflow stages that helper into the macOS package scripts and uses it in the signed package build path at .github/workflows/release.yml:136-161.
  • No non-default environment variable is needed for the attack. The safe reproduction below uses the default /var/tmp/.homebrew_pkg_user.plist path.

False-positive screening performed:

  • The positive PoC uses the real package/scripts/preinstall, package/scripts/postinstall, and Library/Homebrew/utils/macos_user.sh from the checkout. Only platform commands (defaults, id, git, chown, uname) were shimmed to safely run on Linux and avoid privileged filesystem changes.
  • The negative/control case removes the /var/tmp/.homebrew_pkg_user.plist file. With no active macOS console user available in the local test environment, preinstall and postinstall fail with the expected No valid user for Homebrew installation... message, demonstrating that the attacker-controlled plist is what selects the install user.
  • Command injection was not observed: the username is passed as a quoted chown argument. The confirmed issue is improper trust and privilege assignment, not shell injection.
  • Formula/cask execution-by-design, archive extraction, GitHub Actions PR flows, dependency issues, and other reviewed candidates were checked separately and were not confirmed as independent reportable issues in this audit.

Affected version evidence:

  • Tested vulnerable checkout: Homebrew 5.1.15-106-gaf69e65, commit af69e652683b9a0299e8334d820bebc6487eb7d0.
  • The current checkout is not exactly on a release tag, and earlier release tags were not exhaustively tested during this local-only audit. The affected range is therefore unknown; confirmed at commit af69e652683b9a0299e8334d820bebc6487eb7d0.
  • No patched version was identified in this local checkout.

CWE rationale:

  • CWE-377 Insecure Temporary File applies because a predictable file in /var/tmp is trusted across a privilege boundary without secure creation or provenance checks.
  • CWE-266 Incorrect Privilege Assignment applies because root-run installer code assigns ownership of a critical installation tree to an attacker-selected user.

CVSS 3.1 rationale for CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:H/A:L:

  • AV:L: the attacker needs local ability to create /var/tmp/.homebrew_pkg_user.plist on the target Mac.
  • AC:L: no race or complex conditions are required; the attacker can create the file before installation.
  • PR:L: an unprivileged local account is sufficient to write to /var/tmp.
  • UI:R: an administrator or management process must install the Homebrew package.
  • S:C: the low-privileged user's control affects a root-run package installation and the trust boundary of software later used by other users.
  • C:L: direct proof shows ownership control over Homebrew cache/install paths, which can support later limited information exposure through modified tooling, but no direct secret read was performed.
  • I:H: direct proof shows root-run recursive ownership changes to attacker-selected ownership for the Homebrew tree/cache, enabling attacker modification of trusted package-manager files.
  • A:L: attacker ownership can disrupt or corrupt Homebrew-managed files, but the PoC did not demonstrate broad system availability loss.

PoC

This PoC is safe, local, and non-destructive. It does not require macOS root privileges because it shims platform commands and records what the root-run installer script would pass to chown. It uses the real package scripts from a clean checkout and the default /var/tmp/.homebrew_pkg_user.plist path.

Environment used:

  • Repository: /home/kali/auto-github-cve-main/github-repo/brew
  • Commit: af69e652683b9a0299e8334d820bebc6487eb7d0
  • Version output: Homebrew 5.1.15-106-gaf69e65
  • Test date: 2026-06-07
  • Local OS for safe simulation: Linux. The target affected package path is macOS installer scripts; platform commands were shimmed to avoid requiring a macOS root installer session.

Setup/check commands:

cd /home/kali/auto-github-cve-main/github-repo/brew
git rev-parse HEAD
./bin/brew --version

Observed output:

af69e652683b9a0299e8334d820bebc6487eb7d0
Homebrew 5.1.15-106-gaf69e65

Positive trigger using the default vulnerable path:

if [ -e /var/tmp/.homebrew_pkg_user.plist ]; then printf 'refusing: default plist already exists\n'; exit 2; fi
tmp=$(mktemp -d)
cleanup() { rm -rf "$tmp"; rm -f /var/tmp/.homebrew_pkg_user.plist; }
trap cleanup EXIT
fakebin="$tmp/fakebin"
homebrew_dir="$tmp/homebrew"
user_home="$tmp/attacker-home"
mkdir -p "$fakebin" "$homebrew_dir/.git/hooks" "$homebrew_dir/cache_api" "$user_home" "$homebrew_dir/bin"
printf 'cache-marker\n' > "$homebrew_dir/cache_api/sample.json"
printf 'attacker\n' > /var/tmp/.homebrew_pkg_user.plist
cat > "$fakebin/defaults" <<'SH'
#!/bin/sh
if [ "$1" = read ] && [ "$2" = /var/tmp/.homebrew_pkg_user.plist ] && [ "$3" = HOMEBREW_PKG_USER ]; then
  cat "$2"
  exit 0
fi
exit 1
SH
cat > "$fakebin/id" <<SH
#!/bin/sh
if [ "\$1" = -P ] && [ "\$2" = attacker ]; then
  printf 'attacker:*:501:20::0:0:Attacker:${user_home}:/bin/zsh\\n'
  exit 0
fi
exit 1
SH
cat > "$fakebin/git" <<'SH'
#!/bin/sh
printf 'git %s\n' "$*" >> "$POC_LOG"
if [ "$1" = tag ]; then printf '5.1.15\n'; fi
exit 0
SH
cat > "$fakebin/chown" <<'SH'
#!/bin/sh
printf 'chown %s\n' "$*" >> "$POC_LOG"
exit 0
SH
cat > "$fakebin/uname" <<'SH'
#!/bin/sh
printf 'arm64\n'
SH
chmod +x "$fakebin"/*
POC_LOG="$tmp/poc.log" PATH="$fakebin:$PATH" bash package/scripts/postinstall ignored "$homebrew_dir" > "$tmp/postinstall.out" 2>&1
printf 'default_path_positive_exit=0\n'
grep '^chown ' "$tmp/poc.log"
test -f "$user_home/Library/Caches/Homebrew/api/sample.json" && printf 'cache_copy=present\n' || printf 'cache_copy=missing\n'
rm -f /var/tmp/.homebrew_pkg_user.plist "$tmp/poc.log"
set +e
POC_LOG="$tmp/poc.log" PATH="$fakebin:$PATH" bash package/scripts/postinstall ignored "$homebrew_dir" > "$tmp/negative.out" 2>&1
rc=$?
set -e
printf 'negative_without_plist_exit=%s\n' "$rc"
grep -F 'No valid user for Homebrew installation' "$tmp/negative.out" || true

Observed positive and negative output from this environment:

default_path_positive_exit=0
chown -R attacker:admin .
chown -R attacker:staff /tmp/tmp.ZD1dnLaXbD/attacker-home/Library/Caches/Homebrew
cache_copy=present
negative_without_plist_exit=1
No valid user for Homebrew installation. Log in before install or specify an install user.

Expected vulnerable behavior:

  • With /var/tmp/.homebrew_pkg_user.plist containing attacker, postinstall completes and reaches privileged ownership sinks with attacker as the chosen package install user.
  • Without that plist, the same local environment fails because there is no valid console user. This demonstrates that the predictable plist controls the package user decision.

Preinstall control:

if [ -e /var/tmp/.homebrew_pkg_user.plist ]; then printf 'refusing: default plist already exists\n'; exit 2; fi
tmp=$(mktemp -d)
cleanup() { rm -rf "$tmp"; rm -f /var/tmp/.homebrew_pkg_user.plist; }
trap cleanup EXIT
fakebin="$tmp/fakebin"; mkdir -p "$fakebin"; printf 'attacker\n' > /var/tmp/.homebrew_pkg_user.plist
cat > "$fakebin/defaults" <<'SH'
#!/bin/sh
if [ "$1" = read ] && [ "$2" = /var/tmp/.homebrew_pkg_user.plist ] && [ "$3" = HOMEBREW_PKG_USER ]; then cat "$2"; exit 0; fi
exit 1
SH
chmod +x "$fakebin/defaults"
PATH="$fakebin:$PATH" bash package/scripts/preinstall >/tmp/preinstall-positive.out 2>&1; printf 'preinstall_positive_exit=0\n'
rm -f /var/tmp/.homebrew_pkg_user.plist
set +e
PATH="$fakebin:$PATH" bash package/scripts/preinstall > "$tmp/preinstall-negative.out" 2>&1
rc=$?
set -e
printf 'preinstall_negative_exit=%s\n' "$rc"
grep -F 'No valid user for Homebrew installation' "$tmp/preinstall-negative.out" || true

Observed output:

preinstall_positive_exit=0
preinstall_negative_exit=1
No valid user for Homebrew installation. Log in before install or specify an install user.

Cleanup:

  • The PoC trap removes the temporary directory.
  • The PoC removes /var/tmp/.homebrew_pkg_user.plist before exit.
  • Before running, it refuses to proceed if /var/tmp/.homebrew_pkg_user.plist already exists.

Final repro re-check after drafting:

  • The postinstall and preinstall PoCs above were run against the current checkout after the candidate was classified. The observed outputs shown above are from this environment and demonstrate both positive trigger and negative/control behavior.

Impact

A local unprivileged attacker who can create a file in /var/tmp can influence a later root-run Homebrew macOS package installation. If an administrator or device management process installs the Homebrew package while the attacker-controlled /var/tmp/.homebrew_pkg_user.plist is present, the installer selects the attacker's username and recursively assigns ownership of the Homebrew installation and cache directories to that user.

The directly reproduced security impact is integrity loss: root-run installer code reaches recursive chown sinks with an attacker-selected user. For /opt/homebrew, this includes chown -R "${homebrew_pkg_user}:admin" . over the installation tree. For /usr/local/Homebrew, related recursive ownership changes affect Caskroom, Cellar, Frameworks, Homebrew, and var/homebrew.

Practical consequences can include:

  • A local attacker becomes owner of the Homebrew installation tree after an administrator installs the package.
  • The attacker can modify Homebrew-managed files that other users may later execute or trust.
  • Future invocations of brew by administrative users can run through attacker-modified package-manager content, creating a path to further integrity compromise.
  • The attacker can disrupt Homebrew availability by corrupting attacker-owned installation files.

This report does not claim direct remote exploitation, direct command injection in the installer script, or direct read of system secrets. The confirmed boundary crossing is local low-privileged control over root-run package ownership decisions.

Suggested remediation

Avoid trusting a predictable file in /var/tmp for a privileged package-user override unless its provenance is strongly verified.

Recommended fixes:

  • Prefer a root/MDM-controlled location outside world-writable temporary directories for the override, such as a root-owned file under /Library/Preferences or another installer-managed private path.
  • If /var/tmp/.homebrew_pkg_user.plist must remain supported for compatibility, require strict validation before using it:
    • canonicalize the path and reject symlinks;
    • require root ownership;
    • require non-world-writable permissions;
    • require parent directory/path expectations;
    • reject ACLs or metadata that make the file user-writable by untrusted accounts;
    • validate that the selected username maps to the intended active console user or to a trusted MDM-provided identity.
  • Revalidate the selected user in postinstall immediately before chown.
  • Consider removing or deprecating the /var/tmp default and requiring MDM deployments to pass an explicit, trusted environment variable or installer choice only from a root-controlled context.

Regression test suggestion:

  • Add a package-script test that creates a fake /var/tmp/.homebrew_pkg_user.plist owned by an unprivileged user or with unsafe permissions and asserts that homebrew-package-user rejects it.
  • Add a positive test for a trusted root-owned override file, if override support remains required.
  • Add an integration-style shell test that verifies postinstall does not pass an unsafe override user to any chown command.

Credits

  • Thai Son Dinh from VinSOC Labs (R&D)
  • Nguyen Huy Vu Dung from VinSOC Labs (AppSec)

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v4 base metrics

Exploitability Metrics
Attack Vector Local
Attack Complexity Low
Attack Requirements Present
Privileges Required None
User interaction Passive
Vulnerable System Impact Metrics
Confidentiality None
Integrity Low
Availability Low
Subsequent System Impact Metrics
Confidentiality None
Integrity Low
Availability None

CVSS v4 base metrics

Exploitability Metrics
Attack Vector: This metric reflects the context by which vulnerability exploitation is possible. This metric value (and consequently the resulting severity) will be larger the more remote (logically, and physically) an attacker can be in order to exploit the vulnerable system. The assumption is that the number of potential attackers for a vulnerability that could be exploited from across a network is larger than the number of potential attackers that could exploit a vulnerability requiring physical access to a device, and therefore warrants a greater severity.
Attack Complexity: This metric captures measurable actions that must be taken by the attacker to actively evade or circumvent existing built-in security-enhancing conditions in order to obtain a working exploit. These are conditions whose primary purpose is to increase security and/or increase exploit engineering complexity. A vulnerability exploitable without a target-specific variable has a lower complexity than a vulnerability that would require non-trivial customization. This metric is meant to capture security mechanisms utilized by the vulnerable system.
Attack Requirements: This metric captures the prerequisite deployment and execution conditions or variables of the vulnerable system that enable the attack. These differ from security-enhancing techniques/technologies (ref Attack Complexity) as the primary purpose of these conditions is not to explicitly mitigate attacks, but rather, emerge naturally as a consequence of the deployment and execution of the vulnerable system.
Privileges Required: This metric describes the level of privileges an attacker must possess prior to successfully exploiting the vulnerability. The method by which the attacker obtains privileged credentials prior to the attack (e.g., free trial accounts), is outside the scope of this metric. Generally, self-service provisioned accounts do not constitute a privilege requirement if the attacker can grant themselves privileges as part of the attack.
User interaction: This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable system. This metric determines whether the vulnerability can be exploited solely at the will of the attacker, or whether a separate user (or user-initiated process) must participate in some manner.
Vulnerable System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the VULNERABLE SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the VULNERABLE SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the VULNERABLE SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
Subsequent System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the SUBSEQUENT SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the SUBSEQUENT SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the SUBSEQUENT SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:L/SC:N/SI:L/SA:N

CVE ID

No known CVE

Weaknesses

Insecure Temporary File

Creating and using insecure temporary files can leave application and system data vulnerable to attack. Learn more on MITRE.

Credits