GHSA-pwjx-qhcg-rvj4

ADVISORY - github

Summary

There is a certificate revocation enforcement bug in rustls-webpki CRL processing. when both the certificate CRL distribution point and the CRL issuing distribution point contain multiple URI names, IssuingDistributionPoint::authoritative_for() reuses one-shot DER iterators across nested comparisons. If the only matching URI pair appears later in both sequences, the implementation misses the match, treats the CRL as non-authoritative, and under UnknownStatusPolicy::Allow accepts a revoked certificate.

affected versions

revocation support shipped in 0.104.0-alpha.4, and the same iterator-reuse logic is still present at commit e4590782afc1207c3e46ba1249e7c3fb9da95198 on 2026-03-20. i did not identify an upstream fix as of that date.

affected component

technical details

RFC 5280 section 5.2.5 and section 6.3.3(b)(2)(i) require the verifier to check whether one of the names in the IDP matches one of the names in the certificate DP. the current implementation contradicts that requirement in the later-match case because iterator state, not the actual set of URI names, determines whether later names are considered.

the attached PoC uses a single trust anchor and a single relevant CRL path. there is no alternate unconstrained root or duplicate trust path for the same key material, so the acceptance result is attributable to the DP/IDP matching bug rather than chain-builder fallback.

the vulnerable flow is:

  1. authoritative_for() parses idp_general_names once before iterating certificate distribution points.
  2. each certificate DP fullName is parsed into another one-shot DerIterator.
  3. uri_name_in_common() iterates the first IDP URI and drains the DP iterator while looking for a match.
  4. if the matching URI pair is second in both lists, it is never compared.
  5. the CRL is treated as non-authoritative, and UnknownStatusPolicy::Allow converts that into a successful verification result for a revoked certificate.

the repository already has a single-URI happy-path revocation test, which demonstrates the expected rejection path when the first URI matches. the attached PoC shows that simply moving the valid match to second position flips the result from Err(CertRevoked) to Ok(()) under the permissive status policy.

steps to reproduce

the attached poc.zip contains a cargo-based integration harness. after extracting it, run make canonical to produce the vulnerable result and make control for the negative controls.

unzip poc.zip -d poc
cd poc
make canonical
make control

the canonical run emits:

[CALLSITE_HIT]: authoritative_for::uri_name_in_common later_uri_pair_skipped=true
[PROOF_MARKER]: vuln_case=Ok(()) first_uri_control=Err(CertRevoked) later_match_position=second allow_unknown=true
[IMPACT_MARKER]: revoked_cert_accepted=true policy=UnknownStatusPolicy::Allow

the control run emits:

[CALLSITE_HIT]: authoritative_for::uri_name_in_common control_path=true
[NC_MARKER]: first_uri_control=Err(CertRevoked) deny_policy=Err(UnknownRevocationStatus) vuln_blocked=true

recommended fix

avoid reusing exhausted DER iterators across nested DP/IDP comparisons. a minimal fix is to reparse or snapshot the URI name sets for each comparison so that every IDP URI can be compared against the full DP URI set. a regression test should cover the case where the only valid URI match is later in both sequences.

cheers, Oleh Konko

Common Weakness Enumeration (CWE)

ADVISORY - github

Improper Check for Certificate Revocation


Sign in to Docker Scout

See which of your images are affected by this CVE and how to fix them by signing into Docker Scout.

Sign in