Zion Boggan zionboggan.com ↗

Add featured finding: certificate path-length bypass writeup + interactive demo

New featured-finding/ deep-dive page with an in-browser MiniPKI sandbox that
demonstrates the bug class, plus a teaser card on the home page. Coordinated
disclosure in progress; full library/CVE details land after the fix ships.
4ecd2da   Zion Boggan committed on May 31, 2026 (3 weeks ago)
featured-finding/index.html +435 -0
@@ -0,0 +1,435 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Featured Finding: Certificate path-length bypass | Zion Boggan</title>
+<meta name="description" content="A coordinated-disclosure vulnerability finding by Zion Boggan: an RFC 5280 certificate path-length constraint that a widely-deployed crypto library enforces only when an unrelated extension is present. Interactive in-browser demonstration of the bug class.">
+<meta name="robots" content="index, follow">
+<link rel="canonical" href="https://zionboggan.com/featured-finding/">
+<meta property="og:type" content="article">
+<meta property="og:site_name" content="Zion Boggan">
+<meta property="og:title" content="Featured Finding: Certificate path-length bypass (coordinated disclosure)">
+<meta property="og:description" content="A logic-gating flaw in certificate-chain validation, found via variant analysis of a just-released security patch. Interactive demo of the bug class; full details and CVE after the fix ships.">
+<meta property="og:url" content="https://zionboggan.com/featured-finding/">
+<meta property="og:image" content="https://zionboggan.com/assets/og-default.png">
+<meta name="twitter:card" content="summary_large_image">
+<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%230c0e12'/%3E%3Ctext x='16' y='22' font-family='monospace' font-size='15' fill='%236cc7b8' text-anchor='middle'%3Ezb%3C/text%3E%3C/svg%3E">
+<style>
+ :root{
+ --bg:#0c0e12; --bg2:#0f1217; --panel:#14181f; --panel2:#171c24;
+ --line:#222936; --line2:#2c3543;
+ --ink:#e8eaed; --soft:#c3cad4; --muted:#8a94a3; --faint:#5d6675;
+ --accent:#6cc7b8; --accent-dim:#274b47; --red:#ff6b6b; --redbg:#2a0d0d;
+ --maxw:1020px;
+ }
+ *{box-sizing:border-box;}
+ html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;text-size-adjust:100%;}
+ body{margin:0;background:var(--bg);color:var(--ink);overflow-x:hidden;
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+ font-size:16px;line-height:1.65;-webkit-font-smoothing:antialiased;
+ -webkit-tap-highlight-color:rgba(108,199,184,.18);}
+ .mono{font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;}
+ a{color:var(--accent);text-decoration:none;}
+ a:hover{color:#8fe0d2;}
+ .wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px;}
+
+ nav{position:sticky;top:0;z-index:20;background:rgba(12,14,18,.82);
+ backdrop-filter:blur(10px);border-bottom:1px solid var(--line);}
+ nav .wrap{display:flex;align-items:center;justify-content:space-between;height:58px;}
+ nav .brand{font-weight:600;letter-spacing:.2px;}
+ nav .brand .dot{color:var(--accent);}
+ nav .links{display:flex;gap:26px;font-size:13.5px;}
+ nav .links a{color:var(--muted);}
+ nav .links a:hover{color:var(--ink);}
+ @media(max-width:680px){nav .links{display:none;}}
+
+ header.hero{padding:60px 0 44px;border-bottom:1px solid var(--line);
+ background:radial-gradient(900px 380px at 78% -10%, #11201e 0%, transparent 60%);}
+ .crumb{font-size:13px;color:var(--muted);margin-bottom:22px;}
+ .status{font-size:12px;letter-spacing:1.4px;text-transform:uppercase;color:var(--accent);
+ display:inline-flex;align-items:center;gap:9px;margin-bottom:18px;
+ border:1px solid var(--accent-dim);border-radius:999px;padding:6px 14px;background:#0e1a18;}
+ .status .pulse{width:7px;height:7px;border-radius:50%;background:var(--accent);
+ box-shadow:0 0 0 0 rgba(108,199,184,.5);animation:p 2.4s infinite;}
+ @keyframes p{0%{box-shadow:0 0 0 0 rgba(108,199,184,.45)}70%{box-shadow:0 0 0 8px rgba(108,199,184,0)}100%{box-shadow:0 0 0 0 rgba(108,199,184,0)}}
+ h1{font-size:clamp(28px,5vw,42px);line-height:1.08;margin:0 0 12px;letter-spacing:-.6px;font-weight:680;}
+ .hero .lede{max-width:720px;color:var(--soft);font-size:17px;margin:0 0 22px;}
+ .hero .lede b{color:var(--ink);font-weight:600;}
+ .facts{display:flex;flex-wrap:wrap;gap:8px 10px;margin-top:8px;}
+ .facts span{font-size:12px;color:var(--soft);background:var(--panel);border:1px solid var(--line);
+ border-radius:6px;padding:5px 11px;}
+ .facts span b{color:var(--accent);font-weight:600;}
+
+ section{padding:52px 0;border-bottom:1px solid var(--line);}
+ .shead{display:flex;align-items:baseline;gap:14px;margin-bottom:26px;}
+ .shead .idx{font-size:13px;color:var(--accent);letter-spacing:1px;}
+ .shead h2{font-size:14px;letter-spacing:2px;text-transform:uppercase;color:var(--muted);margin:0;font-weight:600;}
+ .shead .rule{flex:1;height:1px;background:var(--line);}
+ p.body{color:var(--soft);max-width:760px;}
+ p.body b{color:var(--ink);font-weight:600;}
+ .callout{border-left:2px solid var(--accent-dim);padding:2px 0 2px 18px;margin:22px 0;color:var(--soft);max-width:740px;}
+
+ /* sandbox */
+ .lab{border:1px solid var(--line2);border-radius:12px;overflow:hidden;background:#0a0c10;
+ box-shadow:0 0 0 1px rgba(108,199,184,.08), 0 26px 64px -28px rgba(0,0,0,.75);}
+ .labbar{display:flex;align-items:center;gap:8px;padding:11px 14px;background:#11151b;border-bottom:1px solid var(--line);}
+ .labbar .d{width:11px;height:11px;border-radius:50%;}
+ .labbar .r{background:#ff5f57;}.labbar .y{background:#febc2e;}.labbar .g{background:#28c840;}
+ .labbar .dlabel{color:var(--faint);font-size:12.5px;margin-left:8px;}
+ .labbar .dbadge{margin-left:auto;color:#06231f;background:var(--accent);font-size:11px;font-weight:700;
+ padding:3px 9px;border-radius:5px;letter-spacing:.5px;}
+ .labgrid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
+ @media(max-width:760px){.labgrid{grid-template-columns:1fr;}}
+ .labcol{padding:20px 22px;}
+ .labcol.left{border-right:1px solid var(--line);}
+ @media(max-width:760px){.labcol.left{border-right:none;border-bottom:1px solid var(--line);}}
+ .labcol h4{margin:0 0 4px;font-size:12px;letter-spacing:1.2px;text-transform:uppercase;color:var(--faint);}
+ .chain{margin:14px 0 6px;}
+ .node{border:1px solid var(--line);border-radius:9px;padding:11px 13px;margin:0 0 6px;background:var(--panel);
+ position:relative;transition:border-color .18s,background .18s;}
+ .node .cn{font-size:14px;font-weight:600;color:var(--ink);}
+ .node .ext{font-size:11.5px;color:var(--muted);margin-top:3px;font-family:ui-monospace,Menlo,monospace;}
+ .node.rogue{border-color:#3a2530;}
+ .node.flagged{border-color:var(--red);background:var(--redbg);box-shadow:0 0 0 1px rgba(255,107,107,.25);}
+ .node.okca{border-color:var(--accent-dim);}
+ .arrow{color:var(--faint);font-size:13px;text-align:center;margin:1px 0;}
+ .toggles{margin:6px 0 0;}
+ .tg{display:flex;align-items:center;gap:11px;padding:11px 0;border-top:1px solid var(--line);cursor:pointer;}
+ .tg:first-child{border-top:none;}
+ .sw{width:42px;height:24px;border-radius:999px;background:#2a313d;position:relative;flex:none;transition:background .16s;}
+ .sw::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
+ background:#cfd6df;transition:transform .16s;}
+ .tg.on .sw{background:var(--accent);}
+ .tg.on .sw::after{transform:translateX(18px);background:#06231f;}
+ .tg .tt{font-size:13.5px;color:var(--soft);}
+ .tg .tt b{color:var(--ink);}
+ .tg .tt small{display:block;color:var(--faint);font-size:11.5px;}
+ .runbtn{margin-top:16px;width:100%;display:inline-flex;align-items:center;justify-content:center;gap:8px;
+ padding:11px 18px;border-radius:8px;font-size:14.5px;font-weight:600;cursor:pointer;
+ background:var(--accent);color:#06231f;border:1px solid var(--accent);}
+ .runbtn:hover{background:#8fe0d2;}
+ .console{font-family:ui-monospace,Menlo,monospace;font-size:12.5px;line-height:1.6;color:var(--soft);
+ background:#070809;border-radius:8px;padding:13px 14px;min-height:172px;white-space:pre-wrap;
+ border:1px solid var(--line);}
+ .console .ok{color:var(--accent);}
+ .console .bad{color:var(--red);}
+ .console .dim{color:var(--faint);}
+ .verdict{margin-top:13px;padding:12px 14px;border-radius:8px;font-size:13.5px;font-weight:600;
+ border:1px solid var(--line);}
+ .verdict.accept{background:var(--redbg);border-color:var(--red);color:#ffb4b4;}
+ .verdict.reject{background:#0e1a18;border-color:var(--accent-dim);color:#a9e7db;}
+ .verdict small{display:block;font-weight:400;color:var(--soft);margin-top:3px;font-size:12px;}
+
+ .ruled{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px;}
+ @media(max-width:680px){.ruled{grid-template-columns:1fr;}}
+ .rcard{border:1px solid var(--line);border-radius:10px;padding:15px 16px;background:var(--panel);}
+ .rcard .h{display:flex;align-items:center;gap:9px;margin-bottom:6px;}
+ .rcard .neg{font-size:10.5px;font-weight:700;letter-spacing:.5px;color:#06231f;background:var(--faint);
+ padding:2px 7px;border-radius:4px;}
+ .rcard h3{margin:0;font-size:14.5px;}
+ .rcard p{margin:0;font-size:13px;color:var(--muted);}
+
+ .timeline{list-style:none;margin:6px 0 0;padding:0;max-width:760px;}
+ .timeline li{display:grid;grid-template-columns:128px 1fr;gap:16px;padding:12px 0;border-top:1px solid var(--line);}
+ .timeline li:first-child{border-top:none;}
+ .timeline .when{font-size:12.5px;color:var(--accent);font-family:ui-monospace,Menlo,monospace;}
+ .timeline .what{font-size:14px;color:var(--soft);}
+ .timeline .what b{color:var(--ink);}
+ .pending{color:var(--faint);}
+ .embargo{border:1px dashed var(--line2);border-radius:10px;padding:16px 18px;color:var(--muted);
+ font-size:13.5px;max-width:760px;background:#0e1116;}
+ .embargo b{color:var(--soft);}
+
+ footer{padding:42px 0 64px;}
+ footer .row{display:flex;flex-wrap:wrap;justify-content:space-between;gap:18px;align-items:center;}
+ footer .links a{color:var(--soft);margin-right:20px;font-size:14px;}
+ footer .note{color:var(--faint);font-size:12.5px;max-width:560px;}
+
+ /* ---- mobile (iPhone / iPad) ---- */
+ @media(max-width:760px){
+ .labbar{flex-wrap:wrap;row-gap:6px;}
+ .labbar .dbadge{margin-left:auto;}
+ }
+ @media(max-width:680px){
+ .wrap{padding:0 18px;}
+ header.hero{padding:44px 0 34px;}
+ section{padding:40px 0;}
+ .dtop{display:none;}
+ h1{font-size:clamp(25px,7.5vw,34px);}
+ .timeline li{grid-template-columns:1fr;gap:2px;}
+ .timeline .when{color:var(--faint);}
+ .labcol{padding:17px 16px;}
+ .runbtn{padding:13px 18px;} /* comfortable tap target */
+ .tg{padding:13px 0;}
+ .console{font-size:12px;min-height:150px;}
+ .node .ext{word-break:break-word;}
+ }
+ @media(max-width:380px){
+ .facts span,.rcard p{font-size:11.5px;}
+ h1{font-size:23px;}
+ }
+</style>
+</head>
+<body>
+
+<nav><div class="wrap">
+ <a class="brand mono" href="/" style="color:var(--ink)">zion_boggan<span class="dot">.</span></a>
+ <span class="links">
+ <a href="/#proof">Demos</a>
+ <a href="/#research">Research</a>
+ <a href="/security-research-notebook/">Notebook</a>
+ <a href="https://github.com/zionboggan">GitHub</a>
+ </span>
+</div></nav>
+
+<header class="hero"><div class="wrap">
+ <div class="crumb mono"><a href="/">home</a> / featured finding</div>
+ <div class="status mono"><span class="pulse"></span>Coordinated disclosure in progress · CVE pending</div>
+ <h1>A certificate path-length limit that disappears<br class="dtop">when you remove an unrelated field</h1>
+ <p class="lede">A widely-deployed open-source cryptography library enforces an <b>RFC&nbsp;5280
+ path-length constraint</b> (the rule that stops a subordinate CA from minting further
+ CAs) <b>only when the certificate also carries a separate, unrelated extension</b>. Drop
+ that extension and the limit is silently skipped: a CA that was explicitly forbidden from
+ delegating can issue rogue sub-CAs that the library accepts as valid. I found it by
+ <b>variant-hunting a security patch the maintainers had just shipped</b>. The fix closed
+ one instance of the gating mistake and left this one behind.</p>
+ <div class="facts mono">
+ <span>Class · <b>CWE-295</b> improper certificate validation</span>
+ <span>Standard · <b>RFC 5280</b> §6.1.4 / §4.2.1.9</span>
+ <span>Impact · <b>CA constraint bypass</b></span>
+ <span>Severity · <b>Medium</b> (~CVSS 6.0)</span>
+ <span>Confirmed on the <b>current shipped release</b></span>
+ </div>
+</div></header>
+
+<!-- ============ INTERACTIVE SANDBOX ============ -->
+<section><div class="wrap">
+ <div class="shead"><span class="idx mono">01</span><h2>See the bug in your browser</h2><span class="rule"></span></div>
+ <p class="body">This is a faithful, self-contained reproduction of the flaw's logic (no
+ library named yet; that waits for the fix). You're looking at a certificate chain where a
+ CA limited to <span class="mono">pathLenConstraint = 0</span>, meaning <i>"you may issue
+ end-entity certificates only, never another CA"</i>, has nonetheless issued a sub-CA.
+ A correct validator must reject this. <b>Toggle the <span class="mono">keyUsage</span>
+ extension</b> on that intermediate and run the validator: watch the path-length check turn
+ itself off.</p>
+
+ <div class="lab">
+ <div class="labbar">
+ <span class="d r"></span><span class="d y"></span><span class="d g"></span>
+ <span class="dlabel mono">minipki · verifyCertificateChain()</span>
+ <span class="dbadge mono">LIVE · runs locally</span>
+ </div>
+ <div class="labgrid">
+ <div class="labcol left">
+ <h4>Certificate chain (attacker-supplied)</h4>
+ <div class="chain" id="chain"></div>
+ <div class="toggles">
+ <div class="tg on" id="tgKU" onclick="toggle('KU')">
+ <div class="sw"></div>
+ <div class="tt"><b>keyUsage</b> extension on the constrained intermediate
+ <small>Real CAs include it; minimal tooling often omits it.</small></div>
+ </div>
+ <div class="tg" id="tgFix" onclick="toggle('Fix')">
+ <div class="sw"></div>
+ <div class="tt"><b>Apply my patch</b>: enforce pathLen unconditionally
+ <small>Enforce the constraint whenever cA + pathLen are set, regardless of keyUsage.</small></div>
+ </div>
+ </div>
+ <button class="runbtn mono" onclick="runVerify()">▶ verifyCertificateChain(chain)</button>
+ </div>
+ <div class="labcol">
+ <h4>Validator output</h4>
+ <div class="console mono" id="con"></div>
+ <div class="verdict" id="verdict" style="display:none"></div>
+ </div>
+ </div>
+ </div>
+ <p class="callout">The two states are byte-identical except for one optional extension. Only
+ the chain <i>with</i> <span class="mono">keyUsage</span> is correctly rejected, which proves the
+ constraint's enforcement is gated on the wrong condition. Flip <b>Apply my patch</b> on and
+ the hole closes in both states.</p>
+</div></div></section>
+
+<!-- ============ HOW IT WORKS ============ -->
+<section><div class="wrap">
+ <div class="shead"><span class="idx mono">02</span><h2>Why it happens</h2><span class="rule"></span></div>
+ <p class="body">Certificate-chain validators decide whether each intermediate is allowed to
+ act as a CA. RFC&nbsp;5280 makes two of those decisions <b>independent</b>: §4.2.1.9 says a
+ <span class="mono">basicConstraints</span> <span class="mono">pathLenConstraint</span> caps
+ how many CAs may follow in the path, and §6.1.4 applies that cap to <b>every</b> intermediate
+ unconditionally. The presence of a <span class="mono">keyUsage</span> extension is governed
+ separately by §4.2.1.3 and has nothing to do with path-length processing.</p>
+ <p class="body">The vulnerable code couples them anyway: the path-length check sits behind a
+ guard that only runs <span class="mono">if (keyUsage extension is present)</span>. The author's
+ reasoning was that having parsed <span class="mono">keyUsage</span> they "know" the basic
+ constraints are present too, but that conflates two orthogonal extensions. Remove
+ <span class="mono">keyUsage</span> and a real, signed, constraint-bearing CA escapes its own
+ limit. It's the same <b>"gate a mandatory check on an optional field"</b> anti-pattern that a
+ recently-published advisory fixed for a sibling check. This is the instance that fix didn't reach.</p>
+ <div class="embargo">
+ <b>Held until the fix ships:</b> the exact library, file/line, the published advisory, the
+ assigned CVE, and the real end-to-end proof-of-concept against the live release. Those will
+ appear here once the maintainers release a patched version. Responsible disclosure first,
+ write-up second. <span class="pending">(Private report submitted to the maintainer.)</span>
+ </div>
+</div></div></section>
+
+<!-- ============ WHAT I RULED OUT ============ -->
+<section><div class="wrap">
+ <div class="shead"><span class="idx mono">03</span><h2>What I ruled out</h2><span class="rule"></span></div>
+ <p class="body">Finding the bug was half the work; <b>not crying wolf on the others was the
+ rest</b>. I ran four more independent deep analyses against the same library's most
+ security-critical paths. Each produced a rigorous negative, and a negative I can defend is
+ worth more than a finding I can't. This is the discipline that keeps a report from getting
+ auto-rejected as noise.</p>
+ <div class="ruled">
+ <div class="rcard">
+ <div class="h"><span class="neg mono">RULED OUT</span><h3>ASN.1 validator desync</h3></div>
+ <p>A recently-rewritten schema validator does ignore trailing elements, but every
+ trust-bearing sink is guarded by an outer length check or sits under a real signature.
+ Not weaponizable.</p>
+ </div>
+ <div class="rcard">
+ <div class="h"><span class="neg mono">RULED OUT</span><h3>RSA PKCS#1 v1.5 forgery</h3></div>
+ <p>Probed the Bleichenbacher / low-exponent class with real e=3 keys. Tight padding +
+ full-byte parsing + exact-digest compare pin the hash to the low-order bytes. No
+ no-private-key forgery.</p>
+ </div>
+ <div class="rcard">
+ <div class="h"><span class="neg mono">RULED OUT</span><h3>Ed25519 malleability</h3></div>
+ <p>Differential-tested against OpenSSL across non-canonical, low-order and cofactor edge
+ cases. The S&lt;L fix plus byte-exact R compare make the accepted-signature set a
+ singleton. No divergence.</p>
+ </div>
+ <div class="rcard">
+ <div class="h"><span class="neg mono">RULED OUT</span><h3>X.509 chain confusion</h3></div>
+ <p>DN/issuer spoofing, trust-anchor confusion, critical-extension smuggling, algorithm
+ downgrade. Trust anchoring is byte-exact; no false-accept beyond the path-length issue
+ above.</p>
+ </div>
+ </div>
+</div></div></section>
+
+<!-- ============ TIMELINE ============ -->
+<section><div class="wrap">
+ <div class="shead"><span class="idx mono">04</span><h2>Disclosure timeline</h2><span class="rule"></span></div>
+ <ul class="timeline">
+ <li><span class="when">2026-05-31</span><span class="what"><b>Discovered &amp; confirmed.</b> Variant analysis of a just-released security patch; working PoC on the current shipped release.</span></li>
+ <li><span class="when">2026-05-31</span><span class="what"><b>Reported privately</b> to the maintainer through their security-advisory channel.</span></li>
+ <li><span class="when">pending</span><span class="what pending">Maintainer triage &amp; fix.</span></li>
+ <li><span class="when">pending</span><span class="what pending">CVE assigned · advisory published.</span></li>
+ <li><span class="when">pending</span><span class="what pending"><b>This page unlocks:</b> library named, file/line, CVE link, live PoC against the patched-vs-vulnerable release.</span></li>
+ </ul>
+</div></div></section>
+
+<footer><div class="wrap">
+ <div class="row">
+ <div class="links">
+ <a href="/">← Back to home</a>
+ <a href="/security-research-notebook/">Research notebook</a>
+ <a href="https://github.com/zionboggan">GitHub</a>
+ </div>
+ </div>
+ <p class="note">Research conducted against open-source code under coordinated disclosure. No
+ systems were attacked; all proofs run against local copies and lab reproductions. Full
+ technical detail is withheld until a fixed release is available.</p>
+</div></footer>
+
+<script>
+// ---- disclosure-safe MiniPKI: a faithful reproduction of the gating flaw ----
+// No third-party code; this models the *logic* of the bug using public RFC 5280 concepts.
+var state = { KU:true, Fix:false };
+
+var chain = [
+ { cn:'victim.example.com', role:'leaf', isCA:false, pathLen:null, depth:0 },
+ { cn:'Rogue Sub-CA', role:'sub', isCA:true, pathLen:null, depth:1 },
+ { cn:'Constrained Intermediate', role:'inter', isCA:true, pathLen:0, depth:2 }, // pathLen=0 => no sub-CAs
+ // Root CA lives in the trust store (depth 3)
+];
+
+function extLine(c){
+ if(c.role==='leaf') return 'basicConstraints: cA=false';
+ if(c.role==='inter'){
+ var s = 'basicConstraints: cA=true, pathLenConstraint=0';
+ s += state.KU ? ' +keyUsage(keyCertSign)' : ' (no keyUsage)';
+ return s;
+ }
+ return 'basicConstraints: cA=true +keyUsage(keyCertSign)';
+}
+
+function renderChain(flagged){
+ var rows = [
+ {cn:'Demo Root CA', ext:'trust anchor · basicConstraints: cA=true', cls:'okca'}
+ ];
+ // render top-down: root -> inter -> sub -> leaf
+ var inter = chain[2], sub = chain[1], leaf = chain[0];
+ rows.push({cn:inter.cn, ext:extLine(inter), cls:'okca'});
+ rows.push({cn:sub.cn, ext:extLine(sub), cls: flagged ? 'flagged' : 'rogue'});
+ rows.push({cn:leaf.cn, ext:extLine(leaf), cls: flagged ? 'flagged' : ''});
+ var html='';
+ rows.forEach(function(r,i){
+ if(i) html += '<div class="arrow">│ issues ▼</div>';
+ html += '<div class="node '+r.cls+'"><div class="cn">'+r.cn+'</div><div class="ext">'+r.ext+'</div></div>';
+ });
+ document.getElementById('chain').innerHTML = html;
+}
+
+function toggle(k){
+ state[k] = !state[k];
+ document.getElementById('tg'+k).classList.toggle('on', state[k]);
+ renderChain(false);
+ document.getElementById('verdict').style.display='none';
+ document.getElementById('con').innerHTML='<span class="dim">// chain changed, click verify to re-run</span>';
+}
+
+function line(t,cls){ return '<span class="'+(cls||'')+'">'+t+'</span>\n'; }
+
+function runVerify(){
+ var con=document.getElementById('con'); var out='';
+ var inter = chain[2];
+ out += line('verifyCertificateChain(): 3 certs + 1 trust anchor','dim');
+ out += line('depth 3 Demo Root CA ............ trusted anchor ✓');
+ out += line('depth 2 Constrained Intermediate . signature ✓, cA=true ✓');
+
+ // The flaw: pathLen enforcement gated on keyUsage presence (unless patched).
+ var enforce = state.Fix || state.KU;
+ var pathLenForSub = inter.depth - 1; // intermediates between inter and leaf, excl. leaf = 1 (the sub-CA)
+ var violated=false;
+ if(inter.pathLen!==null){
+ out += line(' pathLenConstraint=0 present on this CA');
+ if(enforce){
+ // pathLen=0 means: 0 intermediate CAs may follow. The Rogue Sub-CA is one => violation.
+ if(pathLenForSub > inter.pathLen){
+ violated=true;
+ out += line(' enforce pathLen → '+pathLenForSub+' CA(s) follow > limit 0 → VIOLATION', 'bad');
+ }
+ } else {
+ out += line(' keyUsage absent → pathLen check SKIPPED ← the bug', 'bad');
+ }
+ }
+ out += line('depth 1 Rogue Sub-CA ............ signature ✓, cA=true ✓ '+(violated?'(should be rejected)':''));
+ out += line('depth 0 victim.example.com ...... signature ✓');
+
+ var accepted = !violated;
+ con.innerHTML = out;
+ renderChain(accepted /* if accepted, the sub-CA+leaf should not have been trusted -> flag red */);
+
+ var v=document.getElementById('verdict'); v.style.display='block';
+ if(accepted){
+ v.className='verdict accept';
+ v.innerHTML='✗ CHAIN ACCEPTED, and it should not have been.'+
+ '<small>A pathLenConstraint=0 CA just issued a sub-CA, and the validator trusted it. '+
+ (state.Fix?'':'The keyUsage extension is absent, so the path-length check never ran.')+'</small>';
+ } else {
+ v.className='verdict reject';
+ v.innerHTML='✓ CHAIN REJECTED · pathLenConstraint enforced.'+
+ '<small>'+(state.Fix?'The patch enforces the constraint regardless of keyUsage.':'keyUsage is present, so this validator does run the path-length check.')+'</small>';
+ }
+}
+
+renderChain(false);
+document.getElementById('con').innerHTML='<span class="dim">// click ▶ to run the validator on the chain at left</span>';
+</script>
+</body>
+</html>
index.html +39 -0
@@ -207,6 +207,26 @@
footer .row{display:flex;flex-wrap:wrap;justify-content:space-between;gap:18px;align-items:center;}
footer .links a{color:var(--soft);margin-right:20px;font-size:14px;}
footer .note{color:var(--faint);font-size:12.5px;max-width:520px;}
+
+ /* featured finding teaser */
+ .featured{display:block;margin:30px 0 0;border:1px solid var(--line2);border-radius:14px;
+ background:linear-gradient(180deg,#11181c 0%,var(--panel) 100%);overflow:hidden;
+ transition:border-color .15s,transform .15s;}
+ .featured:hover{border-color:var(--accent-dim);transform:translateY(-2px);}
+ .ff-status{display:flex;align-items:center;gap:9px;font-size:12px;letter-spacing:1.2px;
+ text-transform:uppercase;color:var(--accent);padding:12px 24px;border-bottom:1px solid var(--line);background:#0e1a18;}
+ .ff-status .pulse{width:7px;height:7px;border-radius:50%;background:var(--accent);
+ box-shadow:0 0 0 0 rgba(108,199,184,.5);animation:p 2.4s infinite;}
+ .ff-main{display:flex;align-items:center;gap:22px;padding:22px 24px;}
+ .ff-tag{font-size:11px;letter-spacing:1.5px;color:#ff8a8a;margin-bottom:9px;}
+ .ff-main h3{margin:0 0 8px;font-size:21px;letter-spacing:-.3px;line-height:1.2;color:var(--ink);}
+ .ff-main p{margin:0 0 12px;color:var(--soft);font-size:14.5px;max-width:690px;}
+ .ff-main p b{color:var(--ink);font-weight:600;}
+ .ff-facts{display:flex;flex-wrap:wrap;gap:6px 8px;}
+ .ff-facts span{font-size:11.5px;color:var(--muted);background:var(--bg);border:1px solid var(--line);
+ border-radius:5px;padding:3px 9px;}
+ .ff-go{flex:none;color:var(--accent);font-size:14px;white-space:nowrap;}
+ @media(max-width:680px){.ff-main{flex-direction:column;align-items:flex-start;gap:14px;}}
</style>
<!--SEO-->
<!--/SEO-->
@@ -247,6 +267,25 @@
</div>
</div></header>
+<div class="wrap"><a class="featured" href="/featured-finding/">
+ <div class="ff-status mono"><span class="pulse"></span>Coordinated disclosure in progress · CVE pending</div>
+ <div class="ff-main">
+ <div>
+ <div class="ff-tag mono">★ FEATURED FINDING · LIVE VULNERABILITY RESEARCH</div>
+ <h3>A certificate path-length limit that vanishes when you remove an unrelated field</h3>
+ <p>A widely-deployed open-source crypto library enforces an RFC&nbsp;5280 CA path-length
+ constraint <b>only when a separate extension is present</b>, so a CA forbidden from
+ delegating can mint rogue sub-CAs the library still trusts. Found by variant-hunting a
+ security patch the maintainers had just shipped. <b>Interactive proof you can run in the browser.</b></p>
+ <div class="ff-facts mono">
+ <span>CWE-295</span><span>RFC 5280 §6.1.4</span><span>CA constraint bypass</span>
+ <span>Confirmed on the current shipped release</span><span>CVE pending</span>
+ </div>
+ </div>
+ <span class="ff-go mono">Open the finding →</span>
+ </div>
+</a></div>
+
<section id="proof" class="proof"><div class="wrap">
<div class="shead"><span class="idx mono">00</span><h2>See it in action</h2><span class="rule"></span></div>
<p class="proof-intro">Three recordings, all real output, nothing staged: a web-app data