3 min read

Private Receipts Publicly Accessible: A Bug Bounty PII Exposure Story

Some bug bounty programs can feel pretty dry once they've been public for a while: all the obvious issues have been picked clean, there are no things to report, and so on. We tend to believe that, and we tell ourselves: "I'm pretty sure that other hackers have already picked the low hanging fruits and I might need to dig deeper to find bugs."

Well, every now and then, an overlooked public endpoint hides something impactful. This is one of those cases.

While researching a public program, I stumbled upon a backend review feature involving receipt uploads, something meant to be private, short-lived, and only visible to the moderation team. The frontend said it well:

“Receipts will not be made public and will be deleted after moderation.”

That's exactly what I decided to test, but like I said, since this was a public program, I was almost certain that a simple vulnerability there should've been patched or already reported. I was wrong.

The Flow: Review IDs

This platform allows users to leave reviews and optionally upload receipts to validate them. These receipts are intended for internal review and not shown publicly.

Every review has a numeric ID (review_id). Once a review is created, this ID is returned in the response, and is used in subsequent operations, including uploading receipts.

The flow looks like this:

  1. A user creates a review → receives a review_id
  2. The user uploads a receipt via: POST /v1/verify_review/:review_id/receipts
  3. The system stores the file and retrieves it via: GET /v1/verify_review/:review_id/receipts
  4. The file is hosted on an S3 bucket and returned via a signed URL.

The Problem: No Ownership Checks

After mapping out the flow, I started testing what would happen if I manipulated the review_id.

Simple thing, right? I'm sure somebody already checked this. Nope.

Well...

  • I was able to upload a receipt to any review, regardless of ownership.
  • I could fetch receipts from reviews I didn't create, including sensitive documents from other users leaking PII.
  • I was also able to delete uploaded files from reviews I had nothing to do with.

There were no access control checks on the backend whatsoever. As long as I supplied a valid review_id, the system trusted me and returned a URL to access the file.

This is a very simple and basic, but still deadly, vulnerability called IDOR.

The Impact

The receipts I found often included:

  • Full names and email addresses from other users around the world
  • Provider details, addresses, full names
  • Physical addresses and postal codes
  • Signatures and payment information, amounts, card information

All of this was accessible to any registered user with a little effort and a basic scripting setup. Even more concerning: the IDs were sequential. This made enumeration trivial.

The Response

Even though this subdomain was out-of-scope, I reported the issue due to the sensitivity of the data involved.

The response from the security team was this:

They treated it with the urgency it deserved and gave me a bonus for the work. Quite exemplary!

Conclusion

This vulnerability wasn’t flashy. There was no fancy exploit chain. It was a simple, quiet absence of access control in a sensitive workflow, one that took a few hours to find, but only seconds to exploit once understood.

And that's the lesson:

Even in public programs that seem already hacked, there are still impactful vulnerabilities hiding beneath layers of trust, legacy code, or assumptions. You just have to dig.

This is the kind of work that really matters to me: clear, focused research, understand the functionality, and use it to our advantage.