GHSA-6hxq-p678-4hr2

Suggest an improvement
Source
https://github.com/advisories/GHSA-6hxq-p678-4hr2
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-6hxq-p678-4hr2/GHSA-6hxq-p678-4hr2.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-6hxq-p678-4hr2
Downstream
Published
2026-09-04T17:33:20Z
Modified
2026-09-04T17:45:04Z
Severity
  • 2.0 (Low) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N CVSS Calculator
Summary
SimpleWebAuthn: Registration verification does not sufficiently ensure that attestation certificates chain to a trust anchor
Details

Summary

validateCertificatePath() does not verify that an attestation's certificate chain actually terminates at a configured trust anchor. When walking the chain it stops at the first self-signed certificate it finds (which could be user-supplied), and exits early.

This happens before the configured Apple/Google/etc trust anchor (which is concatenated to the end of the chain) is reached.

A user can therefore register a credential and have the server accept it as if it were backed by a genuine Apple / Android SafetyNet / Yubikey / etc.

Details

packages/server/src/helpers/validateCertificatePath.ts:

The configured trust anchor is appended to the end of the untrusted chain (line 83):

const x5cWithTrustAnchor = x5cCertsParsed.concat([anchor]);

The walk then verifies each cert was signed by the next, but breaks on the first self-signed cert (lines 104–116):

if (issuer.subject === issuer.issuer) {
  // Root cert detected, make sure it signed itself
  const issuerSignedIssuer = await issuer.verify(
    { publicKey: issuer.publicKey, signatureOnly: true },
    WebCrypto,
  );
  if (!issuerSignedIssuer) {
    throw new InvalidSubjectAndIssuer();
  }
  break;   // <-- exits before the appended trust anchor is ever checked if user supplied self-signed cert comes first
}

The success condition is therefore "the certs form an internally-consistent chain ending in some self-signed cert" Rather than "the chain terminates at one of the configured trust anchors."

Exploit shape

attacker sends:  x5c = [ forgedLeaf (signed by attacker root),
                         attackerSelfSignedRoot ]

library builds:  [ forgedLeaf, attackerSelfSignedRoot, <configured Apple/Google/etc root> ]

walk:  forgedLeaf -> attackerSelfSignedRoot        (verifies, attacker controls both)
       attackerSelfSignedRoot is self-signed        -> break
       attackerSelfSignedRoot -> configured root    (NEVER CHECKED)

return true. The configured anchor never gets checked.

As far as observed, all attestation enforcement uses validateCertificatePath when using MDS etc.

Database specific
{
    "cwe_ids":  [
        "CWE-295",
        "CWE-296"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-09-04T17:33:20Z",
    "nvd_published_at":  null,
    "severity":  "LOW"
}
References

Affected packages

npm / @simplewebauthn/server

Package

Name
@simplewebauthn/server
View open source insights on deps.dev
Purl
pkg:npm/%40simplewebauthn/server

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
13.3.2

Database specific

last_known_affected_version_range
"<= 13.3.1"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-6hxq-p678-4hr2/GHSA-6hxq-p678-4hr2.json"