October 15, 2026
$9,000 for a Bucket That Trusted the Wrong Thing: Cloud Storage IDOR Exposing Every Userβs Files
Cloud object storage has made a certain category of bug both more common and, somehow, more invisible than it was a decade ago. When everyβ¦

By T4nv1
6 min read
Cloud object storage has made a certain category of bug both more common and, somehow, more invisible than it was a decade ago. When every file lived on a server you controlled end to end, "who can access this path" was at least a question your own code answered. Once storage moves to a managed service, the access-control question splits across two systems β your application's own logic, and the storage provider's separate, independently configured permission model β and it's astonishingly easy for a team to get the first one right while never fully reckoning with the second.
The target, which I'll call Mossgate here per their program's disclosure terms, is a document-sharing platform for small businesses, storing uploaded files in cloud object storage and serving them to users through signed, time-limited URLs generated by the application β a reasonable, common pattern, and one that looked properly implemented right up until I checked what happened when a signed URL wasn't involved at all.
Why Signed URLs Aren't the Whole Access-Control Story
The standard, secure pattern for this kind of architecture generates a pre-signed URL on demand, scoped to one specific object, valid for a short window, cryptographically signed so it can't be tampered with or extended. A user requests a file, the application checks whether they're authorized to see it, and only then generates and returns a signed URL granting temporary access to that one object. Done correctly, this is a solid model β the signature itself enforces the time and object-scope restriction regardless of what the underlying bucket's own permissions look like.
The gap this depends on not existing is whether the underlying storage bucket is also independently, directly accessible without ever going through a signed URL at all β because if the bucket itself is publicly readable, or readable by anyone with valid cloud credentials of any kind, the entire signed-URL system is a secondary gate on a front door that was never actually locked.
Testing the Assumption Directly
I had a legitimate, properly signed URL from using the application normally, which looked like:
https://mossgate-user-files.s3.amazonaws.com/documents/a8f3e91c/report.pdf?X-Amz-Signature=...&X-Amz-Expires=900https://mossgate-user-files.s3.amazonaws.com/documents/a8f3e91c/report.pdf?X-Amz-Signature=...&X-Amz-Expires=900The object key itself β documents/a8f3e91c/report.pdf β was visible and unencrypted within that URL, signature aside. I tested whether requesting that exact same object key directly, with the signature parameters stripped off entirely, still succeeded:
GET https://mossgate-user-files.s3.amazonaws.com/documents/a8f3e91c/report.pdfGET https://mossgate-user-files.s3.amazonaws.com/documents/a8f3e91c/report.pdfIt returned the file. No signature required, no authentication of any kind β the underlying bucket itself was configured with public read access at the bucket-policy level, meaning the entire application-layer signed-URL system, however correctly implemented on its own terms, was sitting in front of a storage backend that never actually needed a signature to serve a file at all.
From "The Bucket Is Public" to "I Can Read Every User's Files"
A publicly readable bucket is a serious finding by itself, but the severity question that actually matters is how predictable the object keys are β if they're genuinely random and unguessable, public bucket access is a real but narrower problem, since an attacker would still need to somehow learn a specific object key before being able to request it. I looked more closely at the key structure across several of my own uploaded files to understand whether a8f3e91c and similar segments were truly random, or followed some discoverable pattern.
Across five uploads of my own, in sequence, the key segments were:
documents/a8f3e91c/report.pdf
documents/a8f3e91d/notes.pdf
documents/a8f3e91e/invoice.pdf
documents/a8f3e91f/contract.pdf
documents/a8f3e920/summary.pdfdocuments/a8f3e91c/report.pdf
documents/a8f3e91d/notes.pdf
documents/a8f3e91e/invoice.pdf
documents/a8f3e91f/contract.pdf
documents/a8f3e920/summary.pdfSequential, not random at all β incrementing by exactly one hexadecimal value with each successive upload, almost certainly derived from an auto-incrementing internal identifier converted to hex rather than any cryptographically random source. Combined with the bucket's public read access, this meant the entire object key space was trivially enumerable: starting from any known key, walking the sequence up or down in either direction would systematically surface every other user's uploaded document on the platform, with no authentication, no signed URL, and no rate limiting standing in the way, since each request was simply an ordinary, unauthenticated GET against a publicly readable storage bucket.
Confirming Without Actually Harvesting Real User Data
This is the point where a finding like this carries real risk of overreach if a researcher gets curious about scale. I confirmed the enumeration pattern worked by requesting a small number of keys immediately adjacent to my own uploads' known values β a handful of requests, deliberately kept minimal, specifically to prove the sequential pattern generalized beyond my own files rather than to actually harvest or review any other user's document contents.
I want to be precise about what that confirmation actually involved: I requested the objects, confirmed each one returned a valid file (checking content-type and file size in the response headers, which was sufficient to prove a real file existed at that key) without opening, downloading in full, or reading the actual contents of any document that wasn't mine. The goal was proving the access-control failure and the key-predictability pattern existed and generalized, not auditing what other businesses' confidential documents actually contained.
The Report
Cloud storage misconfiguration reports benefit from separating the two independent root causes clearly, because conflating "the bucket is public" with "the keys are predictable" understates the finding β either one alone is a real problem, but together they compound into full, systematic data exposure across the entire user base.
Title: Publicly readable storage bucket combined with sequential, predictable object keys allows unauthenticated enumeration and access to all users' uploaded documents
Root cause: The underlying cloud storage bucket is configured with public read access at the bucket-policy level, meaning objects are directly accessible without requiring the application's own pre-signed URL mechanism at all. Object keys are derived from a sequential, auto-incrementing identifier rather than a cryptographically random value, making the entire key space trivially enumerable from any single known key.
Reproduction: A properly obtained, legitimate signed URL; confirmation that the same object remained accessible with all signature parameters stripped; the sequential key pattern observed across five of my own successive uploads; and limited confirmation (content-type and size only, no content read) that the pattern generalized to immediately adjacent keys belonging to other users.
Impact: Complete, unauthenticated exposure of every document ever uploaded to the platform, systematically enumerable by anyone with no account, no credentials, and no interaction with the application beyond direct requests to the storage provider's own domain.
Fix recommendations:
- Restrict the storage bucket's access policy to deny all public and direct read access, requiring every request to go through the application's signed-URL mechanism without exception
- Regenerate object keys using a cryptographically random identifier (a UUID v4 or equivalent) rather than any value derived from a sequential or otherwise predictable source, independent of the bucket-policy fix, as defense in depth
- Audit cloud storage configuration as a standard, recurring part of security review, since this class of misconfiguration lives entirely outside application code and is easy to overlook in a code-focused review process
What Happened After
Mossgate restricted the bucket's public access policy within hours of confirming the report β this is the kind of fix that, once identified, is a configuration change rather than a code deployment, and it moved accordingly fast. The key-randomization fix for new uploads followed within a week, alongside a migration plan to re-key existing stored objects, which they noted would take longer given the volume of files involved. The report closed at $9,000, rated critical, reflecting complete and systematic exposure of the platform's entire document corpus rather than any single-user or single-document impact.
Why Cloud Storage Misconfigurations Deserve Direct Testing, Not Just Code Review
This bug class is specifically dangerous because it's largely invisible to application-layer code review β the vulnerable configuration lives in the cloud provider's own console or infrastructure-as-code definitions, not in anything a typical pull request review would ever touch. A few habits worth carrying into your own testing:
Always test whether a signed URL's underlying object is accessible without the signature. This is a single, cheap test β strip the signature parameters and request the bare object URL β and it directly reveals whether the entire signed-URL system is enforcing anything at all, or merely decorating an already-open door.
Check whether object keys are actually random, not just long. A long but sequential or otherwise structured key provides essentially no real security benefit over a short one β length alone says nothing about predictability, and the only way to know is comparing several keys generated in sequence.
Limit enumeration confirmation to the minimum needed to prove the pattern generalizes. Checking response metadata (content-type, size) for a small number of adjacent keys is sufficient to prove systematic exposure exists; actually reading other users' real document contents crosses from demonstrating a vulnerability into accessing data you have no legitimate reason to see.
Treat cloud infrastructure configuration as an explicit, separate attack surface from application code, worth its own dedicated testing pass rather than assuming a secure-looking application layer implies a secure storage layer underneath it.
What stuck with me about this one is how confidently the application layer's own logic could have passed a focused code review. The signed-URL generation code, by itself, likely looked completely reasonable β correct expiration, correct signing, correct authorization check before issuance. None of that mattered, because the thing it was supposed to be the only path to was never actually locked in the first place.