When CVSS Scores the Endpoint, Not the Loss

Louis Sanchez August 17, 2026 7 min read

Earlier this year we reported an account takeover in Ente, the end-to-end encrypted service for photos, 2FA codes, and secrets. A former emergency contact could collapse a 30-day safety window to zero in one request, reset the victim's password, and walk away with the account — including the Ente Auth seeds protecting that person's bank and email.

We scored it 9.9 Critical. The published CVE record scores it 6.5 Medium, with C:N — no confidentiality impact.

My first reaction was that someone had made a mistake. They had not. The 6.5 is defensible under the specification, and working out exactly why it is defensible turned out to be more useful than arguing about it. So we replaced our number with theirs on the advisory, and wrote this instead.

The short version

CVSS scores what a vulnerability does to the component that contains it. It does not score what the victim ends up losing when the flaw is used as the first step of an obvious chain. For bugs whose damage arrives one step later — through legitimate functionality the flaw unlocked — the base score can land a full severity band below the real consequence. That is not a bug in CVSS. It is a bug in treating a base score as a risk rating.

Two vectors, same bug

Here is the disagreement in full. Same vulnerability, same write-up, two different readings:

SourceScoreVector
Voke Cyber 9.9 Critical AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
CVE record 6.5 Medium AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

The first four metrics are identical. Everything downstream of them differs. We said the compromise crosses a security boundary and costs the victim confidentiality, integrity, and availability. The record says it stays inside one boundary and costs integrity only.

Why the 6.5 is right

Take the vulnerable request on its own terms. The attacker calls an approval endpoint with a recovery session id that is not theirs. The endpoint flips that session from waiting to ready.

What did that request read? Nothing. What did it delete or take offline? Nothing. It changed one row's status field without authorization. That is an integrity impact and, strictly, nothing else. C:N and A:N are correct descriptions of that request.

The photos, the 2FA seeds, the lockout — none of those come from the flawed endpoint. They come from the next step: a password reset, using Ente's ordinary recovery flow, working exactly as designed. The attacker was already holding a legitimately wrapped copy of the recovery key from when they were genuinely added as an emergency contact. Nothing in that second step is a vulnerability. It is the product doing its job for someone who should not have been allowed to reach it.

CVSS asks about the vulnerable component and the impact that component's compromise causes. Answer that question honestly for this endpoint and you get a Medium. The specification even anticipates the tension — it contemplates scoring a chain of vulnerabilities as a whole where that is what you are describing. But there is no second vulnerability here to chain to. There is one flaw and then a supported feature. So the conservative reading wins, and the conservative reading is 6.5.

Why the 6.5 is still misleading

Now read the same vector as a defender with a patch queue.

C:N tells you no data is exposed. In this bug, the entire point of the control that was bypassed was to stop data from being exposed. The waiting period existed for exactly one reason: to give the victim a chance to reject a recovery before somebody reset their password. Removing it does not risk disclosure. It hands over the account, and Ente Auth means it hands over the accounts behind it too.

The chain is not speculative, not conditional on a second vulnerability, not dependent on an unusual configuration. It is one HTTP request away, and it uses documented functionality. Any attacker who bothered with step one is doing step two.

So the number is technically sound and practically deceptive at the same time. If your process is "Criticals this week, Mediums next quarter," a metric that describes the mechanism rather than the outcome will sort this bug into the wrong pile, and it will do it while being perfectly correct.

It happened twice in the same batch

Ente was not a one-off. In the same set of re-scores, our Penpot cross-team file takeover moved from 9.9 Critical to 7.5 High, on this vector:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Availability only. No confidentiality, no integrity. For a bug where an attacker overwrites a victim's design file and re-parents it into their own project, leaving themselves able to open, edit, export, and share it.

Read as "the victim lost their file," A:H alone is coherent. Read as what actually happened, the attacker is holding a copy they can read at will, which is difficult to describe as C:N. Same shape of gap as Ente: the score captures the deprivation and misses the acquisition.

Both advisories now publish the assigned scores, with a breakdown of each assigned metric and what it does and does not capture.
Read the Ente advisory

The more useful number was in the same record

Worth noting what else the enrichment carried. Alongside the 7.5, the Penpot record includes an SSVC assessment: exploitation status proof-of-concept, technical impact partial, and automatable: yes.

"Automatable: yes" is a sentence a defender can act on. It says this can be scripted across every instance an attacker can find, which for a self-hosted product is the thing that should move your maintenance window. That assessment was sitting in the same record as the CVSS score, and it is the one almost nobody pipes into a dashboard.

What to actually do with this

None of this is an argument for ignoring CVSS. It is an argument for knowing what you are holding.

Why we changed our own numbers

We could have kept 9.9 on our advisory and footnoted the disagreement. We publish the assigned score instead, for a straightforward reason: the CVE record is what the rest of the world consumes. A researcher advisory that carries a different headline number than the record it points to just makes the reader do reconciliation work, and it looks like score inflation whether or not it is.

What we kept is the argument. Each advisory now walks the assigned vector metric by metric, says plainly which parts describe the endpoint rather than the outcome, and leaves the impact analysis intact underneath. The number matches the record. The reader still learns what the bug costs.

That is the honest version of a scoring disagreement: publish the number the ecosystem uses, and make sure nobody reading your write-up mistakes it for the whole story.

A number is not a finding

The severity label is the least interesting part of a report. What matters is whether the person reading it understands what an attacker gets and what to do about it. That is how we write every engagement report — findings tied to real consequences, not a scanner's ranked list. We test web applications, APIs, and cloud environments for clients across the Charlotte, NC area and nationwide.

Get a Quote