# Budibase: DNS rebinding SSRF bypasses remain in OpenAPI import and REST query execution

Budibase's OpenAPI import and REST undici fetch paths re-resolve hostnames after blacklist validation, letting a builder-level user DNS-rebind to loopback/private services.

- Payout: Research
- Severity: high
- Program: Budibase
- Found by: DavidCarliez
- Disclosed: 2026-07-24
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-xg5g-26x8-cvf4
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-xg5g-26x8-cvf4
- Tags: area:web domain:web impact:internal-access kind:research platform:ghsa sev:high tech:nodejs technique:chained technique:incomplete-fix technique:source-review vuln:access-control vuln:ssrf

**Target:** Node.js server outbound fetch paths (OpenAPI query import, REST datasource/query execution)
**Root cause:** OpenAPI import validates the hostname via blacklist then raw-fetches separately (validate-then-fetch gap); REST execution passes a custom undici dispatcher that overrides the pinned Node agent, so undici re-resolves the hostname at connect time.
**Steps:**
1. Configure attacker hostname so validation lookup returns a public IP (e.g. `8.8.8.8`) and the later request lookup returns `127.0.0.1`.
2. Start a loopback canary HTTP service on the Budibase host.
3. OpenAPI import: submit import URL `http://rebind.test:<port>/openapi.json` via builder query import endpoints; guard resolves to 8.8.8.8, raw fetch resolves to 127.0.0.1, canary returns `loopback-canary-hit:/openapi.json`.
4. REST: configure a REST datasource/query URL with the same rebinding hostname; direct loopback URL is blocked and pinned Node-agent control does not hit loopback, but the undici dispatcher path re-resolves to 127.0.0.1 and returns `REST-UNDICI-REBIND-CANARY`.
**How found:** Noticed the previous DNS-pinning fix relied on the Node `agent` option, then checked callers: OpenAPI import bypassed the helper entirely (separate validate and fetch), and REST passed a custom undici `dispatcher` that takes precedence over the pinned agent, so the connection re-resolves the hostname.
**Exploitation:** Authenticated builder-level user supplies a rebinding hostname in an OpenAPI import URL or REST datasource/query URL; the server validates the public DNS answer, then re-resolves at connection time to a loopback/private address, issuing server-side requests to internal HTTP services subject to each feature's response handling.
**Insight:** When a fix pins DNS by passing a Node `agent`, audit every caller for transports that ignore it — undici `dispatcher`/custom `fetchFn` overrides the agent and re-resolves at connect time, and any validate-then-fetch split reintroduces the rebinding window.

## Proof of concept (copied exactly from the original report)

**OpenAPI import rebinding proof JSON**

```
{
  "outcome": "allowed",
  "body": "loopback-canary-hit:/openapi.json",
  "lookups": [
    {"phase": "guard", "hostname": "rebind.test", "address": "8.8.8.8"},
    {"phase": "fetch", "hostname": "rebind.test", "address": "127.0.0.1"}
  ]
}
```

**REST undici rebinding proof JSON**

```
{
  "directControl": {"outcome": "blocked", "hitsAfter": 0},
  "pinnedNodeFetchControl": {"outcome": "no_loopback_hit", "hitsAfter": 0},
  "undiciBypass": {
    "outcome": "allowed",
    "status": 200,
    "body": "REST-UNDICI-REBIND-CANARY",
    "hitsAfter": 1
  },
  "lookupTrace": [
    {"phase": "guard", "hostname": "rebind.test", "address": "8.8.8.8"},
    {"phase": "fetch", "hostname": "rebind.test", "address": "127.0.0.1"}
  ]
}
```

