SSRF: private-IP check misses 100.64/10, 198.18/15, 192.0.0/24 and NAT64 ranges #5

Closed
opened 2026-10-16 17:51:15 -07:00 by eric · 0 comments
Owner

Found in security audit run 3 (2026-10-16, 06d630d). Severity: low.

Problem

The private/reserved check in resolve_and_validate_url() relies only on FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE (includes/class-apprise-client.php:463-467). Only ::ffff:0:0/96 is unwrapped first (:454-460). Under PHP 8.5 these non-global ranges are still accepted with "Allow Private IPs" off:

  • 100.64.0.0/10 (CGNAT, Tailscale, and Alibaba Cloud metadata at 100.100.100.200)
  • 198.18.0.0/15, 192.0.0.0/24, 224.0.0.0/4
  • 64:ff9b::/96 and 64:ff9b:1::/48 (NAT64 forms of internal IPv4), 2002::/16, fec0::/10

Verified with the real function: 10.0.0.5 and 169.254.169.254 are rejected, while 100.100.100.200, 198.18.0.10, 192.0.0.8 and 64:ff9b::a9fe:a9fe are accepted.

Fix

  • After the filter_var check, add an explicit CIDR denylist for the IPv4 ranges above.
  • For IPv6, unwrap the IPv4 embedded in NAT64 and 6to4 addresses and recheck it, or deny those prefixes outright.
  • Route matches through the existing apprise_notifications_allow_private_ip decision, so internal Apprise servers still work when private IPs are opted into.
  • Add the matching rejectedUrls cases in tests/Unit/ClientTest.php.
Found in security audit run 3 (2026-10-16, `06d630d`). Severity: **low**. ## Problem The private/reserved check in `resolve_and_validate_url()` relies only on `FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE` (`includes/class-apprise-client.php:463-467`). Only `::ffff:0:0/96` is unwrapped first (`:454-460`). Under PHP 8.5 these non-global ranges are still **accepted** with "Allow Private IPs" off: - `100.64.0.0/10` (CGNAT, Tailscale, and Alibaba Cloud metadata at `100.100.100.200`) - `198.18.0.0/15`, `192.0.0.0/24`, `224.0.0.0/4` - `64:ff9b::/96` and `64:ff9b:1::/48` (NAT64 forms of internal IPv4), `2002::/16`, `fec0::/10` Verified with the real function: `10.0.0.5` and `169.254.169.254` are rejected, while `100.100.100.200`, `198.18.0.10`, `192.0.0.8` and `64:ff9b::a9fe:a9fe` are accepted. ## Fix - After the `filter_var` check, add an explicit CIDR denylist for the IPv4 ranges above. - For IPv6, unwrap the IPv4 embedded in NAT64 and 6to4 addresses and recheck it, or deny those prefixes outright. - Route matches through the existing `apprise_notifications_allow_private_ip` decision, so internal Apprise servers still work when private IPs are opted into. - Add the matching `rejectedUrls` cases in `tests/Unit/ClientTest.php`.
eric closed this issue 2026-10-16 21:48:26 -07:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eric/apprise-wordpress-notifications#5
No description provided.