{"id":"CVE-2026-54353","aliases":["EUVD-2026-39915","GHSA-gfq7-5x4g-3xhf"],"status":"published","title":"Budibase: Potential SSRF DNS rebinding bypass in outbound fetch validation","description":"Budibase is an open-source low-code platform. Prior to 3.39.9, authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding. The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection. This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints. This vulnerability is fixed in 3.39.9.","severity":{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00213,"percentile":0.11654,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-367","CWE-918"],"published":"2026-06-22T23:39:24Z","modified":"2026-10-28T02:31:12Z","fixes":[{"ecosystem":"npm","package":"@budibase/backend-core","fixed_in":["3.39.9"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@budibase/backend-core","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.39.9"}]}]},{"source":"osv","ecosystem":"npm","package":"@budibase/backend-core","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.39.9"}]}]}],"products":[{"vendor":"budibase","product":"budibase"}],"references":[{"url":"https://github.com/Budibase/budibase/security/advisories/GHSA-gfq7-5x4g-3xhf","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54353","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-gfq7-5x4g-3xhf"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54353.json","tags":["advisory"]},{"url":"https://github.com/Budibase/budibase","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-54353","title":"Budibase: Potential SSRF DNS rebinding bypass in outbound fetch validation","description":"Budibase is an open-source low-code platform. Prior to 3.39.9, authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding. The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection. This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints. This vulnerability is fixed in 3.39.9.","status":"published","published":"2026-06-26T20:44:52.069Z","modified":"2026-06-29T13:17:32.753Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-367","CWE-918"],"updated_at":"2026-11-02T13:38:09.774038Z"}],"euvd":[{"id":"EUVD-2026-39915","description":"Budibase is an open-source low-code platform. Prior to 3.39.9, authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding. The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection. This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints. This vulnerability is fixed in 3.39.9.","status":"published","published":"2026-06-26T20:44:52Z","modified":"2026-06-29T13:17:32Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high"}],"updated_at":"2026-11-02T18:20:22.87152Z"}],"ghsa":[{"id":"GHSA-gfq7-5x4g-3xhf","title":"@budibase/backend-core has potential SSRF DNS rebinding bypass in outbound fetch validation","description":"Summary\n\nAuthenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding.\n\nThe outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection.\n\nThis results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints.\n\nDetails\n\nThe issue comes from the outbound fetch validation flow resolving DNS twice:\n\nDuring blacklist validation\nAgain during the real socket connection\n\nThe first lookup result is discarded after validation, so the second lookup is free to resolve to a different IP.\n\nThis creates a classic TOCTOU DNS rebinding issue.\n\nAffected flow in:\n\npackages/backend-core/src/utils/outboundFetch.ts\n```\nasync function throwIfUnsafe(url: string): Promise\u003cvoid\u003e {\n  const parsed = parseUrl(url)\n\n  if (await isBlacklisted(parsed.hostname)) {\n    throw new Error(\"URL is blocked or could not be resolved safely.\")\n  }\n}\n\nfor (let redirects = 0; redirects \u003c= MAX_REDIRECTS; redirects++) {\n  await throwIfUnsafe(nextUrl)\n\n  const response = await fetchFn(nextUrl, nextRequest)\n\n  // ...\n}\n```\nfetchFn uses plain node-fetch with no custom http.Agent / https.Agent, so the underlying socket performs its own independent dns.lookup after validation completes.\n\nThe same pattern also exists in:\n\npackages/server/src/automations/steps/utils.ts\n```\nawait throwIfBlacklisted(nextUrl)\n\nconst response = await fetch(nextUrl, nextRequest)\n```\nThe blacklist implementation resolves hostnames but only returns a boolean:\n\npackages/backend-core/src/blacklist/blacklist.ts\n```\nasync function lookup(address: string): Promise\u003cstring[]\u003e {\n  address = parseAddress(address)\n\n  const addresses = await performLookup(address, { all: true })\n\n  return addresses.map(addr =\u003e addr.address)\n}\n\nexport async function isBlacklisted(address: string): Promise\u003cboolean\u003e {\n  // ...\n\n  if (!net.isIP(address)) {\n    try {\n      ips = await lookup(address)\n    } catch (e) {\n      /* ... */\n    }\n  } else {\n    ips = [address]\n  }\n\n  return ips.some(ip =\u003e blackList!.check(ip, getIpVersion(ip)))\n}\n```\nThe resolved IPs are discarded, so callers cannot pin the later socket connection to the validated addresses.\n\nAn attacker controlling authoritative DNS for a hostname can therefore return:\n\na public IP during validation\na private/internal IP during the actual connection\n\nAnything routing through these helpers inherits the issue, including:\n\noutgoing webhook\nSlack\nDiscord\nMake\nZapier\nn8n\nAI extract\nobject-store fetches\n\nSeveral of these steps return upstream response content directly into automation output, which makes the SSRF non-blind.\n\nPoC\n\nTested locally against a self-hosted build from master.\nNo Budibase-operated infrastructure was touched.\n\nRun Budibase locally.\n\nStart a harmless local HTTP listener:\n\npython3 -m http.server 8080 --bind 127.0.0.1\n\nUse a rebinding hostname such as:\n\n7f000001.cb007264.rbndr.us\n\nwhich rotates between:\n\n127.0.0.1\n203.0.113.100\n\nSteps to reproduce:\n\nLog into Budibase with automation permissions.\nCreate an automation using the Outgoing Webhook step.\nSet the URL to:\nhttp://\u003crebinding-host\u003e:8080/\nTrigger the automation.\n\nObserved result:\n\nThe blacklist validation resolves the hostname to the public IP and allows the request.\nnode-fetch performs a second DNS lookup during socket creation.\nThe second lookup resolves to 127.0.0.1.\nThe TCP connection lands on the local service.\nThe local server response body appears directly in the automation output.\nImpact\n\nThis produces a non-blind read-SSRF primitive against anything reachable from the Budibase host process, including:\n\nloopback services (127.0.0.1)\nRFC1918 ranges\ninternal Kubernetes/VPC services\ncloud metadata endpoints (169.254.169.254)\n\nOn cloud deployments without IMDSv2 enforcement, this may expose temporary IAM credentials via:\n\n/latest/meta-data/iam/security-credentials/\u003crole\u003e\n\nOn multi-tenant hosted deployments, this may also create potential cross-tenant access paths through shared internal infrastructure.","status":"published","published":"2026-06-22T23:39:24Z","modified":"2026-08-12T18:57:28Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-367","CWE-918"],"updated_at":"2026-11-02T14:40:58.888637Z"}],"nvd":[{"id":"CVE-2026-54353","description":"Budibase is an open-source low-code platform. Prior to 3.39.9, authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding. The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection. This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints. This vulnerability is fixed in 3.39.9.","status":"published","published":"2026-06-26T21:16:35.417Z","modified":"2026-06-30T20:05:11.687Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high","assessor":"secondary"},{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high","assessor":"primary"}],"cwes":["CWE-367","CWE-918"],"updated_at":"2026-11-02T14:12:26.508751Z"}],"osv":[{"id":"CVE-2026-54353","title":"Budibase: Potential SSRF DNS rebinding bypass in outbound fetch validation","description":"Budibase is an open-source low-code platform. Prior to 3.39.9, authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding. The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection. This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints. This vulnerability is fixed in 3.39.9.","status":"published","published":"2026-06-26T20:44:52.069Z","modified":"2026-10-28T02:31:12.027513632Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-367","CWE-918"],"updated_at":"2026-11-02T15:45:10.818486Z"},{"id":"GHSA-gfq7-5x4g-3xhf","title":"@budibase/backend-core has potential SSRF DNS rebinding bypass in outbound fetch validation","description":"Summary\n\nAuthenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding.\n\nThe outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection.\n\nThis results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints.\n\nDetails\n\nThe issue comes from the outbound fetch validation flow resolving DNS twice:\n\nDuring blacklist validation\nAgain during the real socket connection\n\nThe first lookup result is discarded after validation, so the second lookup is free to resolve to a different IP.\n\nThis creates a classic TOCTOU DNS rebinding issue.\n\nAffected flow in:\n\npackages/backend-core/src/utils/outboundFetch.ts\n```\nasync function throwIfUnsafe(url: string): Promise\u003cvoid\u003e {\n  const parsed = parseUrl(url)\n\n  if (await isBlacklisted(parsed.hostname)) {\n    throw new Error(\"URL is blocked or could not be resolved safely.\")\n  }\n}\n\nfor (let redirects = 0; redirects \u003c= MAX_REDIRECTS; redirects++) {\n  await throwIfUnsafe(nextUrl)\n\n  const response = await fetchFn(nextUrl, nextRequest)\n\n  // ...\n}\n```\nfetchFn uses plain node-fetch with no custom http.Agent / https.Agent, so the underlying socket performs its own independent dns.lookup after validation completes.\n\nThe same pattern also exists in:\n\npackages/server/src/automations/steps/utils.ts\n```\nawait throwIfBlacklisted(nextUrl)\n\nconst response = await fetch(nextUrl, nextRequest)\n```\nThe blacklist implementation resolves hostnames but only returns a boolean:\n\npackages/backend-core/src/blacklist/blacklist.ts\n```\nasync function lookup(address: string): Promise\u003cstring[]\u003e {\n  address = parseAddress(address)\n\n  const addresses = await performLookup(address, { all: true })\n\n  return addresses.map(addr =\u003e addr.address)\n}\n\nexport async function isBlacklisted(address: string): Promise\u003cboolean\u003e {\n  // ...\n\n  if (!net.isIP(address)) {\n    try {\n      ips = await lookup(address)\n    } catch (e) {\n      /* ... */\n    }\n  } else {\n    ips = [address]\n  }\n\n  return ips.some(ip =\u003e blackList!.check(ip, getIpVersion(ip)))\n}\n```\nThe resolved IPs are discarded, so callers cannot pin the later socket connection to the validated addresses.\n\nAn attacker controlling authoritative DNS for a hostname can therefore return:\n\na public IP during validation\na private/internal IP during the actual connection\n\nAnything routing through these helpers inherits the issue, including:\n\noutgoing webhook\nSlack\nDiscord\nMake\nZapier\nn8n\nAI extract\nobject-store fetches\n\nSeveral of these steps return upstream response content directly into automation output, which makes the SSRF non-blind.\n\nPoC\n\nTested locally against a self-hosted build from master.\nNo Budibase-operated infrastructure was touched.\n\nRun Budibase locally.\n\nStart a harmless local HTTP listener:\n\npython3 -m http.server 8080 --bind 127.0.0.1\n\nUse a rebinding hostname such as:\n\n7f000001.cb007264.rbndr.us\n\nwhich rotates between:\n\n127.0.0.1\n203.0.113.100\n\nSteps to reproduce:\n\nLog into Budibase with automation permissions.\nCreate an automation using the Outgoing Webhook step.\nSet the URL to:\nhttp://\u003crebinding-host\u003e:8080/\nTrigger the automation.\n\nObserved result:\n\nThe blacklist validation resolves the hostname to the public IP and allows the request.\nnode-fetch performs a second DNS lookup during socket creation.\nThe second lookup resolves to 127.0.0.1.\nThe TCP connection lands on the local service.\nThe local server response body appears directly in the automation output.\nImpact\n\nThis produces a non-blind read-SSRF primitive against anything reachable from the Budibase host process, including:\n\nloopback services (127.0.0.1)\nRFC1918 ranges\ninternal Kubernetes/VPC services\ncloud metadata endpoints (169.254.169.254)\n\nOn cloud deployments without IMDSv2 enforcement, this may expose temporary IAM credentials via:\n\n/latest/meta-data/iam/security-credentials/\u003crole\u003e\n\nOn multi-tenant hosted deployments, this may also create potential cross-tenant access paths through shared internal infrastructure.","status":"published","published":"2026-06-22T23:39:24Z","modified":"2026-08-12T19:00:07.922760202Z","cvss":[{"version":"3.1","score":8.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-367","CWE-918"],"updated_at":"2026-11-02T15:46:47.822182Z"}]}}
