back to research
LARP-2026-012web4 min read

Share passwords and OTP checks that only the client enforced

A share-link password, a licensed add-on and several verification codes were enforced by the client. Editing one value in a response was enough each time, because the server never checked again when the protected thing was requested.

Some features ask the server a yes or no question and then let the page act on the answer. Is this password right, is this code valid, does this workspace have the add-on. If the server doesn’t enforce that answer again when the protected thing is actually requested, the page is the only gate, and the page runs in my browser.

I was looking at file sharing on the target’s collaboration platform when a share-link password check came back as failed, and I wondered what the page would do with success. That question led to the four findings below.

Editing a response in the proxy only changes what my browser believes. It becomes a bug when the server then serves the file, saves the email or unlocks the feature without remembering that its own check failed. So the real test is doing the protected thing afterwards: download the file, save the change, call the next endpoint.

Files can be shared by link with a password, so a leaked link isn’t enough on its own. I opened a protected link, typed a wrong password and intercepted the response:

jsonpassword check, edited in the proxy
{"status": "failed"}
{"status": "success"}

With success forwarded, the page loaded the file and I could view and download it. Since the download worked, the file request clearly wasn’t tied to a successful password check on the server.

A second client-side flag

The vendor fixed that path. So I went back and looked at what the page loads before it shows the password screen. A metadata request comes first, and its response decides whether the prompt appears at all:

jsonlink metadata, edited in the proxy
{"protected": "true", "allowDownload": "false"}
{"protected": "false", "allowDownload": "false"}

With protected flipped to false the prompt never appeared and the file opened, same as before. The first fix had covered the password response, but the page still took a second value about the same link at face value.

When a fix lands for a client-trust bug, I go through every other value the page reads to make the same decision.

License check

A paid add-on needs its own license. On page load the app asks whether the current workspace has it:

httpentitlement check
POST /api/entitlements/check HTTP/1.1
Content-Type: application/json

{"workspaceId": "<workspace-id>"}
httpresponse, edited in the proxy
HTTP/1.1 200 OK
Content-Type: application/json

{"hasAccess": false}
{"hasAccess": true}

I changed false to true and the add-on opened with no license. Nothing clever was involved. I read every response that comes back during page load and ask what happens if a boolean has the other value.

Verification codes

Code checks fit this pattern well, because the client usually gets a plain valid or invalid answer and moves on. I found three variants.

The first was changing the account email. I entered a victim’s email address as my new email, typed a random six-digit code and intercepted the check:

httpcode check
POST /api/verify/code HTTP/1.1
Content-Type: application/json

{"code": "111111", "contact": "<victim-email>"}

The response said the code was wrong. I edited it to say the code was right, forwarded it, and the email change went through. The account now had an address I had never proven I control.

The second was phone verification during trial signup. I took a successful verification response from another of my accounts and pasted it over the failed one, and the signup accepted it.

The third was signup on another product in the same program, and it didn’t need a proxy at all. The account worked fully without the email ever being confirmed. The confirmation step existed, it just wasn’t enforced anywhere.

The rest of a product treats a verified email as proof of identity. If verification can be skipped, someone can register with your address, or claim an account that isn’t theirs, and be treated as you.

Fix

The server has to make the decision where the protected thing is served, every time it is served:

  • The file endpoint checks server-side state saying this session passed the password for this link.
  • Paid features check the entitlement on each API call they make, not once on page load.
  • A verification step writes “verified” into server-side state, and later steps read that state. Nothing in a request or a replayed response should be able to set it.

Disclosure

All of this was reported privately to the vendor. The vendor isn’t named here, and paths, field names and IDs are changed. Each request has the same structure as the original.

0xsila
back to research