An unauthenticated attacker can achieve Remote Code Execution (RCE) on the Budibase server by triggering an automation that contains a Bash step via the public webhook endpoint. No authentication is required to trigger the exploit. The process executes as root inside the container.
Vulnerable endpoint — packages/server/src/api/routes/webhook.ts line 13:
// this shouldn't have authorisation, right now its always public
publicRoutes.post("/api/webhooks/trigger/:instance/:id", controller.trigger)
The webhook trigger endpoint is registered on publicRoutes with no authentication
middleware. Any unauthenticated HTTP client can POST to this endpoint.
Vulnerable sink — packages/server/src/automations/steps/bash.ts lines 21–26:
const command = processStringSync(inputs.code, context)
stdout = execSync(command, { timeout: environment.QUERY_THREAD_TIMEOUT }).toString()
The Bash automation step uses Handlebars template processing (processStringSync) on
inputs.code, substituting values from the webhook request body into the shell command
string before passing it to execSync().
Attack chain:
HTTP POST /api/webhooks/trigger/{appId}/{webhookId} ← NO AUTH
↓
controller.trigger() [webhook.ts:90]
↓
triggers.externalTrigger()
↓ webhook fields flattened into automation context
automation.steps[EXECUTE_BASH].run() [actions.ts:131]
↓
processStringSync("{{ trigger.cmd }}", { cmd: "ATTACKER_PAYLOAD" })
↓
execSync("ATTACKER_PAYLOAD") ← RCE AS ROOT
Precondition: An admin must have created and published an automation containing:
code field uses a trigger field template (e.g., {{ trigger.cmd }})This is a legitimate and documented workflow. Such configurations may exist in production deployments for automation of server-side tasks.
Note on EXECUTE_BASH availability:...
3.33.4Exploitability
AV:NAC:HPR:NUI:NScope
S:CImpact
C:HI:HA:H9.0/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H