## https://sploitus.com/exploit?id=587C8DB9-01FF-56C6-B7D9-02DCEDE7C8B5
# CVE-2026-54356 β Budibase: authenticated arbitrary S3 signed upload URL issuance
A low-privilege (BASIC role) authenticated user of a **published** Budibase workspace can call
`POST /api/attachments/:datasourceId/url` and obtain an **S3 pre-signed upload URL** signed with
the workspace datasource's **server-side AWS credentials**, for an attacker-controlled object
key (and, if the datasource has no fixed bucket, an attacker-controlled bucket too).
| | |
|---|---|
| **CVE** | CVE-2026-54356 |
| **Advisory** | [GHSA-6x9p-4r67-5gjx](https://github.com/Budibase/budibase/security/advisories/GHSA-6x9p-4r67-5gjx) |
| **Package** | `@budibase/server` |
| **Type** | CWE-862: Missing Authorization |
| **Affected** | `< 3.41.3` |
| **Fixed** | `3.41.3` |
| **Severity** | High β CVSS 3.1 7.1 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N`) |
| **Privilege required** | Authenticated BASIC app user |
## Root cause
`packages/server/src/api/routes/static.ts`:
```ts
.post(
"/api/attachments/:datasourceId/url",
recaptcha,
authorized(PermissionType.TABLE, PermissionLevel.WRITE), // too low, wrong resource
controller.getSignedUploadURL
)
```
`packages/server/src/api/controllers/static/index.ts` (`getSignedUploadURL`) loads the
datasource by `:datasourceId`, pulls its stored `accessKeyId`/`secretAccessKey`, and signs a
`PutObject` request for the attacker-supplied `bucket`/`key`:
```ts
const { bucket, key } = ctx.request.body || {}
const s3 = new S3({
credentials: {
accessKeyId: datasource?.config?.accessKeyId as string,
secretAccessKey: datasource?.config?.secretAccessKey as string,
},
})
signedUrl = await getSignedUrl(s3, new PutObjectCommand({ Bucket: bucket, Key: key }))
```
Minting a credential-backed signed URL is a privileged action, but the check only requires
generic table-write permission (which BASIC holds) and never verifies the caller is entitled to
use that specific datasource. Expected: `403`. Actual: `200` with `signedUrl`/`publicUrl`.
## Impact
- Attacker gets valid `PutObject` pre-signed URLs signed with the org's stored AWS credentials.
- Can write/overwrite arbitrary object keys in the datasource bucket, and β when no bucket is
pinned in the datasource config β potentially any bucket those credentials can reach.
- The signed URL works off-platform (plain `curl`/`PUT`).
## PoC
See [`poc/`](poc/):
- [`poc/exploit.sh`](poc/exploit.sh) β curl PoC (login β mint signed URL β upload proof file).
- [`poc/poc.py`](poc/poc.py) β same flow in Python.
```bash
python3 poc/poc.py \
--target http://localhost:10000 \
--app-id app_xxx --datasource-id datasource_xxx \
--email basic@example.com --password 'Password123!' \
--bucket my-attachments --key poc/cve-2026-54356/proof.txt \
--upload
```
## Remediation
Upgrade to Budibase `3.41.3+`. If you can't upgrade immediately, scope the S3 datasource's AWS
credentials to least-privilege (bucket/prefix-restricted IAM policy) and monitor for unexpected
`PutObject` activity.
## Credit
Discovered and reported by [@KovachVL](https://github.com/KovachVL).
## Disclaimer
For educational/defensive use and coordinated disclosure only. Only test systems you own or are
explicitly authorized to test.