Skip to content

fix(security): read --bridge-file with the caller's privileges (GHSA-wc5v-93m5-3vc6) - #107

Merged
onyks-os merged 1 commit into
mainfrom
security/bridge-file-privileges
Oct 6, 2026
Merged

onyks-os merged 1 commit into
mainfrom
security/bridge-file-privileges

Conversation

@onyks-os

@onyks-os onyks-os commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Fixes GHSA-wc5v-93m5-3vc6 (affects 0.4.5-0.4.10). The fix was prepared in the advisory's private fork; it lands here as a normal PR so it runs the full CI. The advisory will be published without a CVE once 0.4.11 is released.

The problem

ttp start runs as root and opened --bridge-file as root. Two consequences:

  • a rejected line was printed verbatim in the error message;
  • any line whose first token merely contained : - every line of /etc/shadow - was accepted as a bridge and written into the torrc.

Where sudo is granted for ttp alone, that let the caller read root-only files.

The fix

  • Under sudo or pkexec the file is read by a child running as the caller (SUDO_UID/PKEXEC_UID, with the caller's groups), size-capped at 1 MiB and decoded as UTF-8. The kernel applies the caller's permissions; root never opens the path.
  • A bridge address must be an IP literal and a port in range, as Tor requires (ipaddress-validated, IPv6 in brackets).
  • Errors name the line number, never the line's content.

Verification

  • tests/test_bridge_file.py: shadow-style lines refused, malformed addresses refused, the read runs as the invoking user, errors carry no content.
  • Container, as root with SUDO_UID set: a root-only file is refused (Permission denied); the caller's own file is read.
  • Locally: make lint clean, 1083 passed, coverage 99.83%.

🤖 Generated with Claude Code

…wc5v-93m5-3vc6)

`ttp start` runs as root and opened --bridge-file as root. Two ways a
caller could read a root-only file through it, which matters wherever
sudo is granted for `ttp` alone:

- a rejected line was printed verbatim in the error;
- a line whose first token merely contained ':' was accepted as a
  bridge - every line of /etc/shadow is - and written into the torrc.

Now:

- Under sudo or pkexec (SUDO_UID/PKEXEC_UID), the file is read by a
  child running `cat` as the caller, with the caller's primary and
  supplementary groups, so the kernel applies the caller's permissions
  and root never opens the path. A non-numeric caller id is refused
  rather than read as root. Root with no caller behind it (a root shell,
  systemd) reads directly: there is no lower identity to use. Files over
  1 MiB or not UTF-8 are refused.
- A bridge address must be <IPv4>:<port> or [<IPv6>]:<port> with a port
  in 1-65535, which is all Tor accepts in a Bridge line.
- Errors name the line number, never the line.

Verified for real in a container as root with SUDO_UID set: a 0600
root-only file is refused with the caller's "Permission denied", and
the caller's own file is read. 22 new tests; coverage 99.86%.

Affected: 0.4.5 to 0.4.10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: onyks-os <[email protected]>
@onyks-os
onyks-os merged commit d436117 into main Oct 6, 2026
30 of 31 checks passed
@onyks-os
onyks-os deleted the security/bridge-file-privileges branch October 6, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant