Skip to content

SSRF download-URL guard: close DNS-rebinding TOCTOU, IPv4-compatible-IPv6, and magnet-tracker-DNS residuals #479

Description

@forkwright

Finding

The enqueue_download SSRF guard (crates/paroche/src/net_validate.rs, added for #373) is a strong first line but has three residual gaps that a determined caller could exploit. Severity is reduced because #373 also gates enqueue to admin-only, so these are defense-in-depth hardening, not an open member-level hole.

  1. DNS-rebinding TOCTOU. The guard resolves the host and checks the resolved IPs at enqueue time, but the download engine re-resolves at fetch time. An attacker controlling DNS can pass validation with a public IP, then rebind to an internal address before the fetch.
  2. IPv4-compatible IPv6. ip_is_disallowed reduces IPv4-mapped IPv6 (::ffff:a.b.c.d) to its embedded v4, but not the deprecated IPv4-compatible form (::a.b.c.d, ::/96). ::127.0.0.1 is not classified as loopback and would pass. (Low practical risk — the form is deprecated and rarely routed to localhost — but it is an inconsistency.)
  3. Magnet tracker hostnames are not DNS-resolved. validate_magnet_trackers rejects IP-literal and localhost trackers but does not resolve tracker domains, a deliberate asymmetry with the http(s) path.

Evidence

crates/paroche/src/net_validate.rs: validate_fetch_host resolves + checks (good); ip_is_disallowed uses to_ipv4_mapped only; validate_magnet_trackers has a WHY comment documenting the no-resolve choice.

Why this matters

The rebinding gap is the material one: it makes the http(s) IP check bypassable in principle. Full closure requires pinning the validated IP through to the fetcher so validate-time and fetch-time targets cannot diverge — a cross-crate change touching the download-execution path (ergasia/syntaxis), which is why it was scoped out of the #373 fix.

Desired correction

  • Pin the validated IP address (or a validated-resolver handle) from net_validate through to the fetcher, so the fetch connects to exactly the address that passed the check (closes rebinding).
  • Reduce IPv4-compatible IPv6 to its embedded v4 for classification (after loopback/unspecified checks so ::1/:: are not mis-mapped).
  • Decide and document the magnet-tracker resolution policy (resolve-and-check vs. accept-as-third-party), consistently with the fetch path.

Done when: a rebinding test (validate returns public, fetch target is pinned and cannot reach an internal address); ::127.0.0.1 and NAT64-embedded private addresses are rejected; magnet-tracker policy is explicit and tested.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions