Skip to content

Releases: mangetoncompost/torshield

v0.8.2

Choose a tag to compare

@mangetoncompost mangetoncompost released this 02 Jul 22:30

Fix watchdog notification loop on startup: notification now fires once when TorShield is not running, then stays silent until the app comes back.

v0.8.1

Choose a tag to compare

@mangetoncompost mangetoncompost released this 01 Jul 06:07

Notifications

System notifications via macOS notification center (no plugin, no permission required):

  • Connected - fires after Tor bootstraps successfully, shows the exit node IP
  • Connection Failed - fires if Tor does not start within 30s (Tor not installed, network blocked)
  • Disconnected - fires after all protections are torn down
  • New Identity - fires after manual identity rotation, shows new exit node IP
  • Identity Rotated - fires on automatic rotation (5/15/30 min), shows new exit node IP

v0.8.0

Choose a tag to compare

@mangetoncompost mangetoncompost released this 30 Jun 23:25

Security

Critical

  • dnsmasq config moved to /etc/dnsmasq-torshield.conf (root:wheel) via new ts_helper verbs write-dnsmasq-conf / rm-dnsmasq-conf. Previously written to ~/.config/opsec/dnsmasq.conf (user-writable), allowing a local attacker to inject arbitrary dnsmasq directives (dhcp-script, addn-hosts) executed as root on next connect.
  • gen_icon binary always recompiled from embedded source, never reused from cache. Previously the compiled binary persisted in ~/.config/opsec/gen_icon without integrity verification - binary planting attack.

High

  • TOCTOU in ensure_helper() closed: compile inside a tempdir() (mode 0700) that stays alive until the osascript cp completes. Previously the NamedTempFile was dropped before clang ran, releasing the path reservation without O_EXCL protection.
  • dnsmasq killed via validated PID from pid-file (/bin/kill <pid>), not pkill -f <user-path>. The previous pattern matched all process cmdlines as root; the root("kill", ...) call was also silently ignored (bare name not in ts_helper whitelist).
  • Watchdog verified by content comparison before skipping reinstall. File-existence check alone allowed a replaced watchdog LaunchDaemon script (persistent root shell) to persist undetected.

Medium

  • ~/.config/opsec/ restricted to mode 0700 (was 0755 - world-readable). All secrets (torrc, HMAC key, SAFECOOKIE cookie, hostname backups) now inaccessible to other local users.
  • HOME unset fallback uses getpwuid(getuid()) instead of /tmp (world-listable).
  • Tor binary resolved via absolute path (/opt/homebrew/bin/tor first). Previously relied on PATH - a rogue binary earlier in PATH could intercept traffic while tor_ready() returned true.

Low

  • pf interface name validated (alphanumeric only, default en0).

UI

Menu redesigned to professional English labels: Connect / Disconnect, New Identity, Identity Rotation, Kill Switch, MAC Address Randomization, DNS Leak Protection, Fingerprint Resistance, Advanced (was Dev / Scripts), [OK] / [!] dependency status.

v0.7.0

Choose a tag to compare

@mangetoncompost mangetoncompost released this 30 Jun 23:11

Second security audit. All findings confirmed with reference sources before implementation.

Critical

  • pf kill switch rule order fixed: block all was before pass rules - with quick, pass was never reached (man pf.conf). Tor could not connect to relays; the kill switch was silently broken.
  • pf anchor file now written via ts_helper (new write-pf-anchor verb with O_NOFOLLOW): /etc/pf.anchors/ is root:wheel 755, direct write was silently failing, no rules were ever loaded.

High

  • Removed pass in quick tcp: accepted externally-initiated connections through the kill switch. Stateful tracking handles return packets automatically.
  • Tor relay ports 443, 9001, 80 added to pf pass rules: only 9050 (local SOCKS) was allowed, Tor could not bootstrap.
  • /bin/kill absolute path in ts_helper: execv("kill") fails with ENOENT (execv does not search PATH). dnsmasq was never killed by PID.
  • hex_decode() panic fixed: odd-length input from a malformed Tor response crashed TorShield, triggering the watchdog to flush pf and expose the real IP.
  • IPv6 disabled before MAC spoof: ifconfig down/up triggers NDP Router Solicitations that may expose the fe80:: link-local (RFC 4861 s.6.3.7).

Medium

  • No outbound request at startup: reqwest::no_proxy() was sending the real IP to api.ipify.org before Tor was active. Replaced by local_real_ip() via ipconfig getifaddr (no network request).
  • ts_helper.c bundle integrity check: on-disk source compared against the copy embedded in the binary at compile time. Tampered source is discarded to prevent LPE via recompilation.
  • torrc permissions set to 600: was 644, allowing local modification of ExcludeExitNodes or injection of HiddenServiceDir.
  • Captive portal blocked: captiveagent bypasses the SOCKS5 proxy (Apple system daemon). captive.apple.com blocked in /etc/hosts when the kill switch is active.
  • mDNS hostname anonymized: LocalHostName/ComputerName/HostName set to neutral values at enable, restored at disable. Prevents hostname/model identification on local networks (Fingerprint.com: 65% first-name identification rate).
  • helper_ok() uses symlink_metadata() (lstat): metadata() follows symlinks, a symlink at ts_helper pointing to a legitimate SUID binary would have passed the check.

Firefox

  • DNS prefetch disabled: network.dns.disablePrefetchFromHTTPS defaults to false since Firefox 127 (arkenfox #1860) - prefetch lookups bypass the SOCKS5 proxy. Added network.prefetch-next, browser.send_pings, media.navigator.enabled.

v0.6.0

Choose a tag to compare

@mangetoncompost mangetoncompost released this 30 Jun 22:28

Security

  • SAFECOOKIE on Tor control port : authentication now uses SAFECOOKIE (spec 193, HMAC-SHA256 challenge/response) instead of plain COOKIE. The cookie file is never sent in cleartext over the TCP socket.

  • Config integrity : torshield.json is signed with HMAC-SHA256 using a key stored in the macOS Keychain. Any external alteration is detected at load time - config resets to defaults instead of being silently applied.

  • Secure PRNG : rand_bytes() replaced by getrandom::fill() which calls getentropy(2) directly on macOS. The previous clock fallback produced predictable MAC addresses on /dev/urandom failure.

  • ts_helper SUID - tee removed : /usr/bin/tee was in the SUID whitelist (GTFOBins: arbitrary root file write). Replaced by an internal write-pf-conf verb with /etc/pf.conf hardcoded in C and O_NOFOLLOW on open().

  • ensure_helper() - symlink attack : helper binary now compiled in /tmp with an unpredictable random name (O_CREAT|O_EXCL), symlink check post-compilation before elevation.

  • pf anchor - table placement : table <apple_relay> moved from anchor file into /etc/pf.conf. Tables in anchors cause silent boot failures on macOS.

  • user.js strip() : exact prefix match on user_pref("...") lines only - no longer removes comments or third-party prefs.

  • CanvasBlocker downloaded via Tor : XPI now fetched through socks5h://127.0.0.1:9050.