The Error Code That Meant Yes — JavaScript Bug Hunt
Modelled on GnuTLS CVE-2014-0092 (March 2014). GnuTLS's certificate-verification code used helpers that were read as booleans — non-zero meant "yes" — but…
- Language: JavaScript
- Layer: Backend
- Difficulty: Easy
- Concepts: Security, Error Handling
- Modelled on: GnuTLS · CVE-2014-0092
- Visible tests: a well-formed chain to a trusted root verifies; an undecodable intermediate fails the chain
- Reward: 50 XP for a complete fix
Briefing
Modelled on GnuTLS CVE-2014-0092 (March 2014). GnuTLS's certificate-verification code used helpers that were read as booleans — non-zero meant "yes" — but on certain errors they returned a negative error code. Callers tested the result with a plain if, so an error was taken as success, and specially crafted certificates were accepted as valid. It was disclosed weeks after Apple's "goto fail" and fixed in GnuTLS 3.2.12 and 3.1.22.
verify.js walks a chain using two helpers that follow the C convention documented in x509.js: 1 for yes, 0 for no, negative for an error.
Fix verifyChain so an error is never read as "yes".
Bug report
BUG-GNUTLS-0092 · Priority: Critical (TLS bypass) · Reported by: security audit
verifyChain(chain, trustedRoots) — chain[0] is the leaf, each certificate is issued by the next, the last must be in trustedRoots (by subject):
- every issuer must be a CA: x509.checkIfCa(issuer) === 1
- every signature must check: x509.checkSignature(child, issuer) === 1
- 0 (no) AND any negative value (error) both fail the chain
- an empty chain fails
Observed: a chain whose intermediate cannot be decoded verifies as valid.
Logs
[tls] verify chain leaf=shop.example depth=3
[x509] check_if_ca(intermediate) = -69 (ASN.1 DER error)
[tls] chain OKThe code as shipped
src/tls/verify.js (editable)
var x509 = require("./x509");
// chain[0] is the leaf; each certificate is issued by the next one; the last
// must be one of trustedRoots (by subject).
exports.verifyChain = function (chain, trustedRoots) {
if (chain.length === 0) return false;
for (var i = 0; i + 1 < chain.length; i++) {
var child = chain[i];
var issuer = chain[i + 1];
if (!x509.checkIfCa(issuer)) return false;
if (!x509.checkSignature(child, issuer)) return false;
}
return trustedRoots.indexOf(chain[chain.length - 1].subject) !== -1;
};
Read-only context: src/tls/x509.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.