During the initial reconnaissance phase, I spent a few hours just getting a feel for the application and mapping its attack surface. It had the usual suspects: "Send to email," "Ask AI," "Set reminders," and, crucially, "Share with others" (collaboration).One feature particularly fascinated me: user collaboration. The application allowed users to share notes and attachments with varying levels of access. By default, free-tier users were restricted to granting "View Only" permissions. Accessing the second and third privilege tiers required an upgrade to a "Pro" subscription. This is exactly the kind of paywall boundary I love to test.

I decided to capture the legitimate request for granting standard "View Only" access. Firing up Burp Suite, I observed the following body being sent in a POST request:

Endpoint: POST /api/v1/resources/{resource_id}/share/private/users

JSON

JSONString={"users":[{"email_id":"victim@example.com","permission":"READ_ONLY"}]}

As soon as I saw the permission parameter being defined directly in the client-side request, I knew exactly what to try next.

I intercepted a new sharing request and modified the value from "READ_ONLY" to "READ_WRITE".

BOOM 💥. The server returned a 201 Created status. I checked my victim account, and sure enough, it now had the second-level "Edit" privileges—a paid feature unlocked for free.

But I wasn't satisfied yet. I wanted the highest privilege tier: full control to share, edit, and view. I started by trying common enumerations for this, like ADMIN, OWNER, or FULL_ACCESS, but none of my guesses worked.

After about an hour of hitting a wall, I took a step back and restarted my hunt. Instead of guessing, I decided to look for the answer in the source. I began digging through the application's minified JavaScript files. After sifting through tons of code, I finally struck gold. I found a reference indicating that the internal name for the highest privilege level was actually "CO_OWNER".

I threw that value into my repeater tab, hit send, and just like that, I had elevated access.

Key Takeaways

  • Never Trust the Client: This is rule number one. The backend explicitly trusted the permission value sent by the frontend without validating if the user's subscription tier authorized that level of access.
  • Business Logic is Often Fragile: Monetization controls and paywalls are frequent points of failure. When a feature is grayed out in the UI due to subscription status, always check if the API enforces that restriction or if it's just cosmetic.
  • When Guessing Fails, Read the Source: I wasted an hour guessing enum values like OWNER or ADMIN. A targeted search through the client-side JavaScript files revealed the correct "CO_OWNER" value in minutes. Static analysis of JS is a crucial reconnaissance skill.
  • Incremental Escalation: Don't just stop at the first bypass. Once I confirmed I could get READ_WRITE access, I pushed harder to find the maximum impact possible (CO_OWNER), increasing the severity of the report.

Let's Hunt ! > Satyam > 🔗 Follow me on X