Live › vulnerability › CVE-2026-113652
Go to dashboarduploadfs (the ApostropheCMS file storage library) with the Azure backend and sas: true embeds the full account-level SAS token, including its signature, into the public base URL used for every attachment link, so every site visitor receives the credential. An attacker who loads any page can read, overwrite, delete and list every blob in the container for the token's lifetime, or only read and download if the SAS was provisioned read-only.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-23 19:11 UTC, 3h before NVD. All times on this page are UTC.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| uploadfs | npm | >= 1.25.0, < 1.28.0 | 1.28.0 |
1.25.0 and < 1.28.0sig= in attachment URLs1.28.0 or laterEach row is the first time that source carried this signal. The analyst’s rows are its own findings.
When uploadfs is configured with the Azure backend and sas: true (a supported configuration introduced in uploadfs 1.25.0), createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. As a result, getUrl() returns a base URL carrying the complete account/container SAS signature (?sv=...&sig=...), and Apostrophe's attachment.url() prepends it to every public attachment link — broadcasting a container-level SAS credential (commonly sp=rwdlac, full read/write/delete/list) to every site visitor, including anonymous ones. Anyone who can see an attachment URL can read, overwrite, delete, and list every blob in the container for the SAS lifetime. A companion functional bug (blob path appended after the query string, producing broken URLs) shows this code path was never exercised end to end. Shared-key mode (default) and non-Azure backends are not affected.
Verified at commit 5a3746aaed49761e171c2cbfe793267c959829fd (uploadfs 1.27.0 in the apostrophe monorepo).
Root cause: a credential-bearing client URL is reused as the public base URL. The chain:
new BlobServiceClient("https://<account>.blob.core.windows.net?<SAS query string>") where cluster.key is the full SAS string including siggetUrl() takes getContainerClient(...).getBlobClient('').url as baseUrl; SDK v12 derived clients inherit the SAS query string, so baseUrl already contains ?sv=...&sig=...attachment.url() = uploadfs.getUrl() + path for every rendered attachment link; attachment/index.js#L1503 — getBrowserData() also ships uploadsUrl to every logged-in editor sessionAttack surface: every public page rendering attachment URLs (images, download links), plus the uploadsUrl delivered to editor sessions. The leak is passive and unconditional — no action required from the victim beyond serving a page. The credential further propagates through HTML source, logs, Referer headers, and CDN/proxy access logs.
Suggested remediation:
const u = new URL(blob.svc.getContainerClient(...).getBlobClient('').url); u.search = ''; (or compose accountUrl + '/' + container manually), and append the blob path to the pathname, not after the query.sr=b per-blob), and prefer generating short-lived per-blob read-only signed URLs via generateBlobSASQueryParameters instead of embedding a service-level SAS in permanent public URLs.Full PoC posted as the first comment below (poc.js + README; offline, no Azure account needed). It replicates the library's exact two steps with the declared dependency @azure/storage-blob@12:
npm install @azure/storage-blob@12 && node poc.jsnew BlobServiceClient("https://myaccount.blob.core.windows.net?sv=...&sig=FAKESIGNATURE") exactly like createContainer(), then evaluates the getUrl() logic (getContainerClient('uploads-container').getBlobClient('').url) and asserts the resulting base URL still contains the sig parameter.baseUrl: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE
attachment URL as served to visitors: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE/attachments/abc123-photo.jpg
The produced URL both leaks the token (sv…sig) and demonstrates the broken path placement. Cross-checking attachment/index.js confirms this URL reaches public pages unmodified.
sp=rwdlac (read/write/delete/list/ACL) — is disclosed to every site visitor including anonymous users, valid for the SAS lifetime (often years). Holders can read, overwrite, delete, and enumerate every attachment in the container; the library applies no narrowing.sas: true (introduced in 1.25.0; present through 1.27.0). Default shared-key mode and other backends are unaffected. When the SAS is provisioned read-only, the impact reduces to unauthenticated read/download of all attachments.Reported by Kimi Security Team ([email protected]).
This page as .md · .json · in your agent: get_signal("CVE-2026-113652")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.
/container/?sv=...&sig=XXX/attachments/id-name), so these URLs are also broken — evidence the 1.25.0 code path was never tested