DEV Community

correctover
correctover

Posted on

Auditing a Published CVE: A Six-Step Reproducibility Walkthrough

: A Six-Step Reproducibility Walkthrough

In July 2026, an advisory for a widely deployed SM2 library described private-key recovery by "observing a few Math.random() outputs." We audited that claim. The premise was wrong, the real root cause was narrower and stranger, and the maintainer confirmed our correction in writing. This is the method, on public record, step by step.

The claim under test

The advisory described seeding from Math.random() with a pool filled at module load — implying an attacker who samples a few outputs could reconstruct keys. Recoverable-from-outputs is a strong, testable claim.

Step 1 — Pin the artifact, not the story

We froze the exact published npm tarballs (sm-crypto@0.4.0, jsbn@1.1.0, hash-recorded) and instrumented a victim process that pinned Math.random and the wall clock before the libraries loaded. No version drift, no "works on my machine."

Step 2 — Pre-register the counters

Before running anything, we wrote down what we expected to observe and what would falsify the claim: Math.random call count at seeding time, OS RNG calls, generated keys across processes, and signatures under fixed pins. Counters first, experiment second — so the experiment could not be quietly re-interpreted after the fact.

Step 3 — Run the adversary model honestly

The advisory's attack needed observable RNG outputs. Our instrumentation showed the pool fill never called Math.random in Node at all — the count was zero. The premise was not merely unverified; it was impossible as described. What remained was a clock-only seed with dead pool-fill code, which we demonstrated: identical keys across processes pinned to the same wall clock, valid signatures from those keys, both reproduced independently.

Step 4 — State the boundary before anyone asks

Our claim, worded to survive hostile reading: keys generated at or near process startup in runtimes without getRandomValues are recoverable within a millisecond-scale time window by a third party who can bound generation time. Not "any key, any time." The narrower claim is the one that stayed true.

Step 5 — Compare against the known art

The registered attack class (weak-RNG key recovery from observed outputs) was well documented. Our variant required no output observation at all — strictly weaker preconditions than anything in the record. When your result is stronger than the reference class, that comparison is part of the finding, not a footnote.

Step 6 — Ship the bundle, not the adjectives

Everything above ships as a canonicalized, SHA-256 anchored, Ed25519-signed evidence package with re-run instructions — anyone can re-derive every digest and re-verify the signature without trusting us. The maintainer independently confirmed the corrected root cause and adopted the expanded impact statement.

What this is a demonstration of

Not a product pitch — a discipline: frozen artifacts, pre-registered counters, honest boundaries, and a signed bundle instead of adjectives. When your deliverable is trust, the method is the product.

wangguigui@correctover.com · https://correctover.com

Top comments (0)