GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-6HXQ-P678-4HR2

Vulnerability from github – Published: 2026-09-04 17:33 – Updated: 2026-09-04 17:33
VLAI
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.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 13.3.1"
      },
      "package": {
        "ecosystem": "npm",
        "name": "@simplewebauthn/server"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "13.3.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "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"
  },
  "details": "## Summary\n`validateCertificatePath()` does not verify that an attestation\u0027s 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.\n\nThis happens before the configured Apple/Google/etc trust anchor (which is concatenated to the end of the chain) is reached.\n\nA 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.\n\n## Details\n`packages/server/src/helpers/validateCertificatePath.ts`:\n\nThe configured trust anchor is appended to the end of the untrusted chain (line **83**):\n\n```ts\nconst x5cWithTrustAnchor = x5cCertsParsed.concat([anchor]);\n```\n\nThe walk then verifies each cert was signed by the next, but breaks on the first self-signed cert (lines **104\u2013116**):\n\n```ts\nif (issuer.subject === issuer.issuer) {\n  // Root cert detected, make sure it signed itself\n  const issuerSignedIssuer = await issuer.verify(\n    { publicKey: issuer.publicKey, signatureOnly: true },\n    WebCrypto,\n  );\n  if (!issuerSignedIssuer) {\n    throw new InvalidSubjectAndIssuer();\n  }\n  break;   // \u003c-- exits before the appended trust anchor is ever checked if user supplied self-signed cert comes first\n}\n```\n\nThe 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.\"\n\n## Exploit shape\n\n```\nattacker sends:  x5c = [ forgedLeaf (signed by attacker root),\n                         attackerSelfSignedRoot ]\n\nlibrary builds:  [ forgedLeaf, attackerSelfSignedRoot, \u003cconfigured Apple/Google/etc root\u003e ]\n\nwalk:  forgedLeaf -\u003e attackerSelfSignedRoot        (verifies, attacker controls both)\n       attackerSelfSignedRoot is self-signed        -\u003e break\n       attackerSelfSignedRoot -\u003e configured root    (NEVER CHECKED)\n```\n\n`return true`. The configured anchor never gets checked.\n\n\nAs far as observed, all attestation enforcement uses validateCertificatePath when using MDS etc.",
  "id": "GHSA-6hxq-p678-4hr2",
  "modified": "2026-09-04T17:33:20Z",
  "published": "2026-09-04T17:33:20Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MasterKale/SimpleWebAuthn/security/advisories/GHSA-6hxq-p678-4hr2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MasterKale/SimpleWebAuthn/commit/67a41fed3dfd96cab1dcf414f6d1792a72e06e35"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MasterKale/SimpleWebAuthn/commit/8a53d70f42bcbb7c744ef1b90e6db35bc4b26d06"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MasterKale/SimpleWebAuthn/commit/dd0d73c716a528e6645efafbd43a972b64df71f9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MasterKale/SimpleWebAuthn"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MasterKale/SimpleWebAuthn/releases/tag/v13.3.2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "SimpleWebAuthn: Registration verification does not sufficiently ensure that attestation certificates chain to a trust anchor"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…