Summary
On a routine brew update, a previously-installed tap whose git remote is plain-HTTP / MITM-able / attacker-influenced can have its persistent origin silently rewritten to an attacker URL, and the same update fetches, pulls, and rebases the tap onto the attacker's commits — with no user action and no prompt. The HOMEBREW_ALLOWED_TAPS allowlist is not re-validated against the redirected remote, so it does not prevent this. Formula/cask code is then executed from the attacker's content on subsequent operations.
Details
When git emits redirecting to <url> to stderr during a tap fetch, Homebrew rewrites the tap's origin:
Library/Homebrew/cmd/update.sh (~:923) writes <tap_path>\t<redirected_remote> to .git/REDIRECTED_REMOTES.
Library/Homebrew/cmd/update-report.rb:79 consumes it and calls tap.apply_redirected_remote! (.git/REDIRECTED_REMOTES is unlinked at :92 after applying).
Library/Homebrew/tap.rb:574: safe_system "git", "-C", path, "remote", "set-url", "origin", redirected_remote — the rewrite, with no re-check.
allowed_by_env? / forbidden_by_env? are evaluated once, against the requested remote, before the clone/fetch — never against the post-redirect remote. So a server-supplied redirect both (a) moves the tap's trusted origin to an attacker host and (b) defeats the HOMEBREW_ALLOWED_TAPS mitigation shipped alongside this code. The retarget also fires on the explicit brew tap <name> <url> path.
I recommends re-evaluating allowed_by_env?/forbidden_by_env? against the redirected remote before git remote set-url, on both the apply_redirected_remote! path and the update-report.rb consumption of .git/REDIRECTED_REMOTES, and/or requiring explicit confirmation for a cross-host retarget. Independently, passing -- before the value at tap.rb:574 would close a bounded option-injection sub-case. Final severity and remediation are the PSRT's to decide.
PoC
Loopback, synthetic — no public hosts. Two loopback git-HTTP servers: an "origin" that first serves a tap cleanly, then (flag-flipped) returns a 301 redirect to an "attacker" repo one commit ahead.
brew tap poc/example http://127.0.0.1:<ORIGIN>/repo.git → git -C <tap> remote get-url origin = ORIGIN (clean).
- Flip the origin server to redirect; run
brew update.
- Observed:
==> Redirected tap poc/example remote to http://127.0.0.1:<ATTACKER>/repo.git/; git -C <tap> remote get-url origin = ATTACKER; tap HEAD = the attacker commit (pulled+rebased). With HOMEBREW_ALLOWED_TAPS set to allow only ORIGIN, the retarget to the off-allowlist ATTACKER still succeeds.
(Full recorded stdout + the loopback server script are available on request / in the private review bundle.)
Impact
Silent, persistent takeover of an installed tap's source of truth on a routine, no-interaction brew update, with the documented HOMEBREW_ALLOWED_TAPS mitigation bypassed — leading to execution of attacker-controlled formula/cask code. Who is impacted: any user with a tap whose remote is reachable over a channel an attacker can redirect (a plain-HTTP remote, or an on-path/MITM position).
AI/LLM use disclosure
This finding was researched and validated with AI/LLM assistance; the reporter verified its correctness against the source and the recorded run, and takes full responsibility for it.
- How: an AI agent system (Anthropic Claude, via Claude Code) performed native source-code review to surface the candidate, then orchestrated a live PoC on a disposable, isolated Linux VM (loopback servers, synthetic inputs only). AI output was treated as a fallible first draft; the sink, reachability, and PoC result were human-verified before reporting.
- When: 2026-06-26 – 2026-06-27, against master HEAD c0ed315.
- Where: in the research (discovery + PoC validation) and in drafting this write-up.
- Prompts: operative instruction — "find every 0-day from the latest HEAD; every finding must have a PoC/wire-proof with recorded reachability stdout; conform with the security model and vuln-filing conventions; use disposable VMs within policy to fan out."
For this finding: stand up a loopback git-HTTP origin that serves a tap cleanly then 301-redirects to an attacker repo, run brew update, and assert whether origin is silently retargeted and whether HOMEBREW_ALLOWED_TAPS is bypassed. Full prompt/agent logs available to the PSRT on request.
Summary
On a routine
brew update, a previously-installed tap whose git remote is plain-HTTP / MITM-able / attacker-influenced can have its persistentoriginsilently rewritten to an attacker URL, and the same update fetches, pulls, and rebases the tap onto the attacker's commits — with no user action and no prompt. TheHOMEBREW_ALLOWED_TAPSallowlist is not re-validated against the redirected remote, so it does not prevent this. Formula/cask code is then executed from the attacker's content on subsequent operations.Details
When git emits
redirecting to <url>to stderr during a tap fetch, Homebrew rewrites the tap'sorigin:Library/Homebrew/cmd/update.sh(~:923) writes<tap_path>\t<redirected_remote>to.git/REDIRECTED_REMOTES.Library/Homebrew/cmd/update-report.rb:79consumes it and callstap.apply_redirected_remote!(.git/REDIRECTED_REMOTESis unlinked at:92after applying).Library/Homebrew/tap.rb:574:safe_system "git", "-C", path, "remote", "set-url", "origin", redirected_remote— the rewrite, with no re-check.allowed_by_env?/forbidden_by_env?are evaluated once, against the requested remote, before the clone/fetch — never against the post-redirect remote. So a server-supplied redirect both (a) moves the tap's trusted origin to an attacker host and (b) defeats theHOMEBREW_ALLOWED_TAPSmitigation shipped alongside this code. The retarget also fires on the explicitbrew tap <name> <url>path.I recommends re-evaluating
allowed_by_env?/forbidden_by_env?against the redirected remote beforegit remote set-url, on both theapply_redirected_remote!path and theupdate-report.rbconsumption of.git/REDIRECTED_REMOTES, and/or requiring explicit confirmation for a cross-host retarget. Independently, passing--before the value attap.rb:574would close a bounded option-injection sub-case. Final severity and remediation are the PSRT's to decide.PoC
Loopback, synthetic — no public hosts. Two loopback git-HTTP servers: an "origin" that first serves a tap cleanly, then (flag-flipped) returns a 301 redirect to an "attacker" repo one commit ahead.
brew tap poc/example http://127.0.0.1:<ORIGIN>/repo.git→git -C <tap> remote get-url origin= ORIGIN (clean).brew update.==> Redirected tap poc/example remote to http://127.0.0.1:<ATTACKER>/repo.git/;git -C <tap> remote get-url origin= ATTACKER; tap HEAD = the attacker commit (pulled+rebased). WithHOMEBREW_ALLOWED_TAPSset to allow only ORIGIN, the retarget to the off-allowlist ATTACKER still succeeds.(Full recorded stdout + the loopback server script are available on request / in the private review bundle.)
Impact
Silent, persistent takeover of an installed tap's source of truth on a routine, no-interaction
brew update, with the documentedHOMEBREW_ALLOWED_TAPSmitigation bypassed — leading to execution of attacker-controlled formula/cask code. Who is impacted: any user with a tap whose remote is reachable over a channel an attacker can redirect (a plain-HTTP remote, or an on-path/MITM position).AI/LLM use disclosure
This finding was researched and validated with AI/LLM assistance; the reporter verified its correctness against the source and the recorded run, and takes full responsibility for it.
For this finding: stand up a loopback git-HTTP origin that serves a tap cleanly then 301-redirects to an attacker repo, run brew update, and assert whether origin is silently retargeted and whether HOMEBREW_ALLOWED_TAPS is bypassed. Full prompt/agent logs available to the PSRT on request.