Skip to content

Commit b5baf8a

Browse files
1 parent b6f1d94 commit b5baf8a

1 file changed

Lines changed: 7 additions & 3 deletions

File tree

advisories/github-reviewed/2026/03/GHSA-pwjx-qhcg-rvj4/GHSA-pwjx-qhcg-rvj4.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-pwjx-qhcg-rvj4",
4-
"modified": "2026-03-20T21:51:17Z",
4+
"modified": "2026-03-23T17:40:52Z",
55
"published": "2026-03-20T21:51:17Z",
66
"aliases": [],
7-
"summary": "webpki has a certificate revocation enforcement bug",
8-
"details": "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.\n\n## affected versions\n\nrevocation 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.\n\n## affected component\n\n- component: CRL authority matching for certificate DP vs CRL IDP names\n- repo pin: `https://github.com/rustls/webpki` @ `e4590782afc1207c3e46ba1249e7c3fb9da95198`\n- callsite: `src/crl/types.rs:626`, `src/crl/types.rs:649`, `src/crl/types.rs:664`\n- pinned source: [https://github.com/rustls/webpki/blob/e4590782afc1207c3e46ba1249e7c3fb9da95198/src/crl/types.rs#L626-L678](https://github.com/rustls/webpki/blob/e4590782afc1207c3e46ba1249e7c3fb9da95198/src/crl/types.rs#L626-L678)\n\n## technical details\n\nRFC 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.\n\nthe 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.\n\nthe vulnerable flow is:\n\n1. `authoritative_for()` parses `idp_general_names` once before iterating certificate distribution points.\n2. each certificate DP fullName is parsed into another one-shot `DerIterator`.\n3. `uri_name_in_common()` iterates the first IDP URI and drains the DP iterator while looking for a match.\n4. if the matching URI pair is second in both lists, it is never compared.\n5. the CRL is treated as non-authoritative, and `UnknownStatusPolicy::Allow` converts that into a successful verification result for a revoked certificate.\n\nthe 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.\n\n## steps to reproduce\n\nthe 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.\n\n```bash\nunzip poc.zip -d poc\ncd poc\nmake canonical\nmake control\n```\n\nthe canonical run emits:\n\n```text\n[CALLSITE_HIT]: authoritative_for::uri_name_in_common later_uri_pair_skipped=true\n[PROOF_MARKER]: vuln_case=Ok(()) first_uri_control=Err(CertRevoked) later_match_position=second allow_unknown=true\n[IMPACT_MARKER]: revoked_cert_accepted=true policy=UnknownStatusPolicy::Allow\n```\n\nthe control run emits:\n\n```text\n[CALLSITE_HIT]: authoritative_for::uri_name_in_common control_path=true\n[NC_MARKER]: first_uri_control=Err(CertRevoked) deny_policy=Err(UnknownRevocationStatus) vuln_blocked=true\n```\n\n## recommended fix\n\navoid 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.\n\ncheers,\nOleh Konko",
7+
"summary": "webpki: CRLs not considered authoritative by Distribution Point due to faulty matching logic",
8+
"details": "If a certificate had more than one `distributionPoint`, then only the first `distributionPoint` would be considered against each CRL's `IssuingDistributionPoint` `distributionPoint`, and then the certificate's subsequent `distributionPoint`s would be ignored.\n\nThe impact was that correct provided CRLs would not be consulted to check revocation. With `UnknownStatusPolicy::Deny` (the default) this would lead to incorrect but safe `Error::UnknownRevocationStatus`. With `UnknownStatusPolicy::Allow` this would lead to inappropriate acceptance of revoked certificates.\n\nThis vulnerability is thought to be of limited impact. This is because both the certificate and CRL are signed -- an attacker would need to compromise a trusted issuing authority to trigger this bug. An attacker with such capabilities could likely bypass revocation checking through other more impactful means (such as publishing a valid, empty CRL.)\n\nMore likely, this bug would be latent in normal use, and an attacker could leverage faulty revocation checking to continue using a revoked credential.",
99
"severity": [
1010
{
1111
"type": "CVSS_V3",
@@ -60,6 +60,10 @@
6060
{
6161
"type": "PACKAGE",
6262
"url": "https://github.com/rustls/webpki"
63+
},
64+
{
65+
"type": "WEB",
66+
"url": "https://rustsec.org/advisories/RUSTSEC-2026-0049.html"
6367
}
6468
],
6569
"database_specific": {

0 commit comments

Comments
 (0)