How a missing duplicate-input check in Bitcoin Core let one block crash 0.14.x nodes and silently inflate the supply on 0.15.0–0.16.2 (CVE-2018-17144)
A Bitcoin Core block containing a transaction whose two inputs spend the identical previous output reaches a per-transaction validation path that a 2017 pre-relay optimisation left without a duplicate-input check: on Bitcoin Core 0.14.x this trips an internal assertion and crashes the node outright, and on 0.15.0–0.16.2 — when the duplicated output was created in an earlier block rather than the same block — the weaker check that remained lets it through silently, allowing the block's miner to claim that output's value twice. This is CVE-2018-17144, publicly disclosed by Bitcoin Core on 20 September 2018 and fixed in 0.16.3 and 0.17.0.
Why this matters
Every full node and miner running an affected version is exposed simply by being on the peer-to-peer network — no special access or authentication is required to relay a block to a peer. The trigger is a single crafted block, not a sustained flood, so a transport-level connection- or message-rate limit does nothing to stop it: the fix has to be in block-validation logic itself, which is exactly what 0.16.3/0.17.0 changed.
The disclosure also illustrates why "just a crash bug" reports need re-checking for worse: what began as a reported assertion-failure/crash was, hours later, identified as also enabling silent currency inflation — a miner could get double credit for one already-spent output with no assertion failure, no error, and no visible signal that anything was wrong. Bitcoin Core's own notice calls this "critical" and says it "remains critical that affected users upgrade," independent of whether an operator had only heard the "crash" half of the story.
How the attack works
- Entry point. The block-relay path: an inbound
blockP2P message reaching per-transaction validation of the block's non-coinbase transactions. - Preconditions. The attacker needs a block that is otherwise valid enough to reach that validation step (correct proof-of-work and structure) — in the historical incident this put the practical bar at parties capable of producing blocks, i.e. miners; NVD's own description characterises it as "exploitable by miners." Whether the bug was ever triggered maliciously in the wild, as opposed to being caught by responsible disclosure first, is not established by the sources available.
- Processing path. The malicious transaction has two inputs that reference the exact same previous output (the same txid and output index) — a duplicate-input transaction. Bitcoin Core 0.14's pre-relay block-acceptance optimisation (PR #9049) removed a duplicate-input check that had existed since 2012 (PR #443), on the assumption that later validation stages would still catch it.
- Resource/trust boundary crossed. They didn't, fully. In 0.14.x, the transaction reaches the UTXO-spend bookkeeping and hits an internal assertion — Bitcoin Core's own reproduction driver identifies it as an
assert(!coin.IsNull())-style check — because the same coin is being marked spent twice within one transaction. In 0.15.0, the assertion logic was changed to check only that the spent output exists in the UTXO set, not that it was previously unspent. That is weaker: if the duplicated output was created in the same block as the malicious transaction, the assertion still fires and the node still crashes; if it was created in an earlier block, the output already exists (nothing marks it as double-spent) and the check passes clean. - Resulting behaviour. Two distinct outcomes depending on version and the age of the duplicated output: an assertion-failure crash (0.14.x always; 0.15.0–0.16.2 only for a same-block duplicate), or — for 0.15.0–0.16.2 with a duplicate created in an earlier block — silent acceptance of the block with the double-spent output's value counted twice: an inflation of the effective coin supply with no error raised. A patched node (0.16.3/0.17.0 or later) rejects the block outright with a
bad-txns-inputs-duplicatevalidation error.
Affected systems
- Chain: Bitcoin.
- Implementation: Bitcoin Core. (NVD's CVE-2018-17144 record also lists Bitcoin Knots, a Bitcoin Core fork, across the same version range; this brief and NullRabbit's reproduction cover Bitcoin Core specifically and make no independent claim about Knots.)
- Affected versions, per Bitcoin Core's own notice: 0.14.0–0.14.2 — assertion-failure crash only, on any block with a duplicate-input transaction. 0.15.0, 0.15.1, 0.16.0, 0.16.1, 0.16.2 — crash if the duplicated output was created in the same block; silent supply inflation if it was created in an earlier block. NVD's CVE-2018-17144 description frames the same range as "0.14.x before 0.14.3, 0.15.x before 0.15.2, and 0.16.x before 0.16.3."
- CVSS: 7.5 HIGH (CVSS 3.1,
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H— network-reachable, low complexity, no privileges or user interaction, high availability impact, no confidentiality/integrity impact recorded in the vector; the inflation consequence is a consensus/integrity issue the CVSS vector's availability-only scoring does not fully capture). - Fixed in: Bitcoin Core 0.16.3 and 0.17.0rc4, both released 18 September 2018, via Bitcoin Core PR 14247.
- Scope. This is a consensus/block-validation bug reached over the ordinary P2P block-relay path — not an RPC, wallet, or script-execution issue, and not exploitable by a normal transaction-relay flood; it requires a block.
Evidence
- Publicly documented. Bitcoin Core's official notice (bitcoincore.org, 20 September 2018) states the mechanism, the affected-version behaviour split above, and the fix versions. It records that the bug was reported anonymously on 17 September 2018 to Pieter Wuille, Greg Maxwell and Wladimir van der Laan as a crash bug, and that Matt Corallo separately identified the inflation implication the same day. NVD's CVE-2018-17144 record independently confirms the affected-version range and assigns CVSS 3.1 7.5 HIGH / CVSS 2.0 5.0 MEDIUM.
- Independently reproduced by NullRabbit. NullRabbit's
bitcoin_dup_input_crashreproducer (chains/bitcoin/lab/drivers/known_class_btc.py) frames a complete P2Pblockmessage — a header with valid regtest-difficulty proof-of-work, a coinbase transaction, and a second transaction whose two inputs reference an identicalCOutPoint— and sends it over a standard version/verack P2P handshake to abitcoindregtest node (ruimarinho/bitcoin-core:latest, perchains/_shared/research/known-class-net-corpus-2026-06-29/MODEL-CARD.md), capturing the resulting P2P traffic viatcpdump. That target image is a current, already-patched build, so what was reproduced and captured is the attack traffic and the node'sbad-txns-inputs-duplicaterejection — the wire-level trigger — not the historical crash or inflation outcome, which would require standing up an actual vulnerable 0.14–0.16.2 binary. NullRabbit did not do that, and makes no measurement claim about the crash or the inflation event itself. Four bundles are shipped for this primitive in theNullRabbit/nr-bundles-publicHF dataset (NRDAX-T0056, fidelitylab, discovery originreverse-engineered-cve), used as one class in NullRabbit's public-CVE network-attack detector corpus. - Measured under controlled conditions. Not established. NullRabbit has no independent measurement of the assert-crash, the inflation amount, or any timing/resource figures; all impact statements above are Bitcoin Core's own, as published in its notice.
- Inferred but not reproduced. None beyond what the public disclosure itself states.
Operator actions
- Run Bitcoin Core 0.16.3, 0.17.0, or any later release. The vendor notice documents no config-level or transport-layer workaround — the vulnerable code path is block-validation logic itself, so the only documented fix is the software patch (Bitcoin Core PR 14247). Every currently supported Bitcoin Core release is well past this floor.
- No rate limit or firewall rule substitutes for the patch. The trigger is the content of a single block, not connection or message volume.
- Detection idea (speculative, not from the vendor notice): a network monitor watching P2P block traffic could flag a received block containing a transaction with two structurally identical inputs (same previous txid and output index) as the CVE-2018-17144 wire signature, independent of whether the local node is patched. This is NullRabbit's own framing of what the reproduction bundle's wire pattern could support, not a mitigation Bitcoin Core has documented.
Canonical references
- NRDAX technique: NRDAX-T0056 — duplicate-input-validation-bypass-crash
- Evidence bundle (Hugging Face): NullRabbit/nr-bundles-public — primitive
bitcoin_dup_input_crash - Upstream (primary source): Bitcoin Core security notice, CVE-2018-17144 (20 September 2018)
- Upstream: NVD — CVE-2018-17144
- Upstream: Bitcoin Core 0.16.3 release announcement
No NullRabbit advisory exists for this finding — it is a faithful reproduction of an already-public, already-fixed CVE, not a NullRabbit discovery.
Related attacks
Within NullRabbit's own corpus, bitcoin_dup_input_crash shares its consensus_abuse family and chain with three other Bitcoin Core primitives built from separate public disclosures and also shipped on NullRabbit/nr-bundles-public: btc_mutated_block (CVE-2024-52921, unrequested merkle-mismatched blocks), btc_blocktxn_double_fillblock (a blocktxn-path crash), and btc_version_timestamp_overflow (CVE-2024-52912, an adjusted-network-time overflow). NRDAX-T0056 is a chain-agnostic technique record and may carry instances beyond Bitcoin Core; this brief describes and makes claims about the Bitcoin Core instance only.
Track attacks affecting bitcoin-core
New Bitcoin Core and Bitcoin P2P findings are catalogued as they are reproduced. Track attacks affecting bitcoin-core on NRDAX and in NullRabbit research.
Analysis plus reproducer. No weaponised proof-of-concept code.
Related Posts
How a single crafted gossipsub PRUNE backoff value panics rust-libp2p nodes via an Instant overflow (CVE-2026-34219)
rust-libp2p's gossipsub implementation stores a peer-supplied PRUNE backoff duration and later adds a further duration to it during heartbeat processing using unchecked arithmetic; a single crafted PRUNE with a backoff value near u64::MAX overflows that addition and panics the process.
How CometBFT's SecretConnection handshake spends CPU before authenticating the peer
CometBFT completes the full SecretConnection STS handshake — an X25519 Diffie–Hellman exchange and an Ed25519 signature verification — for any connecting peer before checking whether that peer's node-ID is allowlisted, so an unauthenticated remote source can spend the node's asymmetric-crypto budget at will.
How an unauthenticated TLS half-open flood pins rippled's memory and crashes it under low file-descriptor limits
rippled's inbound TLS listeners on the peer port (51235) and the RPC-HTTPS port (5006) accept a partial TLS handshake with no deadline and no per-IP half-open cap, so a single source IP can pin server memory indefinitely and, at a default file-descriptor limit, crash the process.
