The Safety Window That Closed in One Request: CVE-2026-16751 in Ente

Louis Sanchez July 26, 2026 7 min read

Emergency access is one of the hardest things to build safely. You want a trusted person to be able to rescue you if you are locked out, but you do not want that same person to be able to quietly take your account whenever they like. The usual answer is time: let the trusted contact start a recovery, but make it wait, and tell the account owner, so they can say no before it completes.

Ente does exactly that — a waiting period on emergency-contact recovery, commonly 30 days, during which you are emailed and can reject. We found a flaw that removed the waiting period entirely. A single request turned a multi-day safety window into zero, and a former trusted contact into an account takeover.

The short version

CVE-2026-16751 is an authorization bypass (CWE-639) in Ente's emergency-contact recovery. The endpoint that approves a recovery never checked that the recovery session belonged to the caller — so a former emergency contact could start a recovery and, in the very next request, flip it from waiting to ready, skipping the 30-day notice. They then reset the victim's password, and Ente's own recovery flow wiped the victim's two-factor. Full takeover in under a minute, including the victim's Ente Auth 2FA seeds that protect all their other accounts. CVSS 9.6. Ente fixed it in PR #11311.

Already fixed

Ente confirmed and patched this. Hosted users on api.ente.io are covered. If you self-host Ente, update to a build that includes PR #11311. Unlike a lot of the disclosures we write up, this one had a clean, cooperative vendor response — reported, confirmed, and fixed.

What Ente is protecting

Ente is a privacy-first, end-to-end encrypted service — photos, authentication codes (Ente Auth), and a password and secrets product (Ente Locker). The whole point is that nobody, not even Ente, can reach your data without your keys. People move to a service like this specifically because they want the guarantee.

That is what makes the blast radius here unusual. An Ente account is not just photos. Ente Auth holds the 2FA seeds that guard the victim's bank, email, and everything else. Take over the Ente account and you inherit the keys to the accounts behind it. This is not a single-app problem.

How emergency recovery is supposed to work

You nominate an emergency contact you trust. During that setup, your recovery key is deliberately wrapped to their public key — that is by design, and it is what lets them help you recover. Because handing someone that capability is dangerous, Ente puts a delay in front of it. When an emergency contact starts a recovery, it does not take effect immediately. It enters a waiting period, you get emailed, and you can reject it before it completes.

The waiting period is the entire safety mechanism. Everything else about the feature is intentionally powerful. The delay plus the notification is the one thing standing between "my trusted contact can help me" and "my former trusted contact can seize my account." So the interesting question when auditing this feature is simple: can anything skip the wait?

The bug: an approval that never checks whose recovery it is

Recovery moves through a small state machine — a contact starts it, it waits, it becomes ready, then the password can change. The flaw is in the step that moves a recovery from waiting to ready.

The approval handler checks that the caller says they are approving as themselves. What it never does is load the recovery session named in the request and confirm it actually belongs to them. It takes the session id straight from the request body and runs a database update that matches only on the session id and its current status — nothing ties the session to the caller. Any known waiting session can be flipped to ready by anyone, and flipping to ready resets the wait timer to now.

Then the safety check downstream only enforces the waiting period while the status is still waiting. Once the row says ready, that check is skipped entirely. The password-change step proceeds, and as part of completing recovery it disables the victim's TOTP and removes their passkey two-factor — no further ownership check. The attacker can already decrypt the recovery key, because the victim's key was wrapped to the attacker's public key back during legitimate setup. The waiting period was the only thing holding all of that in check, and one request removed it.

There is a small, almost cruel detail on top: the "recovery approved" notification is addressed using the attacker-supplied identifier, so it goes to the attacker, not the victim. The person losing their account gets no signal that their safety window just collapsed.

What the attack looks like

The prerequisite is not a trick. The attacker was, at some point, genuinely added as the victim's emergency contact and accepted — a normal use of the feature. The rest runs in seconds:

  1. The attacker logs in and starts a recovery for the victim. Ente creates a waiting session with a 30-day timer and emails the victim. The attacker does not wait.
  2. The attacker reads back their own recovery sessions through a normal info endpoint and gets the new session's id.
  3. The attacker calls the approve endpoint with that session id. The missing ownership check lets it through; the session flips to ready and the timer resets to now.
  4. The attacker completes a password change. Ente's recovery flow disables the victim's two-factor, and the credentials are replaced.

At the end, the attacker owns the account. The victim may not have even opened the warning email.

For the vulnerable state transition, the CVSS rationale, and the audit fingerprint operators can look for, see the full advisory.
Read the advisory

Why "safe" features deserve the hardest look

There is a pattern worth taking from this. The most dangerous place to have a missing authorization check is inside the feature that exists to be the safety mechanism, because everyone reasons about it as already-safe. Emergency recovery is supposed to be the careful path. Its entire value is the delay and the notification. So a single transition that trusts an unverified identifier does not just create a bug — it silently deletes the only protection the feature had.

Every step in Ente's recovery flow looked correct on its own. The contact really was a contact. The key wrapping really was intended. The waiting period really was enforced — while the status was waiting. The failure lived in one transition that let an attacker change the status without proving the session was theirs. That is the kind of flaw you only find by following the state machine end to end and asking, at each hop, whether the thing being acted on belongs to the person acting.

The disclosure

For the full technical detail — the vulnerable transition, the CVSS vector, and remediation — see the full advisory.

The safest-looking feature is where the check goes missing

Break-glass flows, recovery paths, and admin overrides are where authorization checks quietly get skipped, because everyone assumes they are already careful. Finding the gap takes reading the flow end to end and testing what each step actually verifies. That is the same discipline we bring to web application and API testing for clients across the Charlotte, NC area and nationwide.

Get a Quote