Skip to content

Commit 44a479e

Browse files
committed
Protect against Core's Merkle leaf node weakness
Bitcoin Core's Merkle tree implementation has no way to discern between internal and leaf node entries. As a consequence it is susceptible to an attacker injecting additional transactions by crafting 64-byte transactions matching an inner Merkle node's hash (see https://web.archive.org/web/20240329003521/https://bitslog.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/). To protect against this (highly unlikely attack vector), we check that the transaction isn't 64 bytes in length, and skip it otherwise.
1 parent 50d21b7 commit 44a479e

File tree

2 files changed

+28
-0
lines changed

2 files changed

+28
-0
lines changed

lightning-transaction-sync/src/electrum.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,18 @@ where
270270
for txid in &sync_state.watched_transactions {
271271
match self.client.transaction_get(&txid) {
272272
Ok(tx) => {
273+
// Bitcoin Core's Merkle tree implementation has no way to discern between
274+
// internal and leaf node entries. As a consequence it is susceptible to an
275+
// attacker injecting additional transactions by crafting 64-byte
276+
// transactions matching an inner Merkle node's hash (see
277+
// https://web.archive.org/web/20240329003521/https://bitslog.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/).
278+
// To protect against this (highly unlikely) attack vector, we check that the
279+
// transaction is at least 65 bytes in length.
280+
if tx.total_size() == 64 {
281+
log_error!(self.logger, "Skipping transaction {} due to retrieving potentially invalid tx data.", txid);
282+
continue;
283+
}
284+
273285
watched_txs.push((txid, tx.clone()));
274286
if let Some(tx_out) = tx.output.first() {
275287
// We watch an arbitrary output of the transaction of interest in order to

lightning-transaction-sync/src/esplora.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,22 @@ where
372372
return Err(InternalError::Failed);
373373
}
374374

375+
// Bitcoin Core's Merkle tree implementation has no way to discern between
376+
// internal and leaf node entries. As a consequence it is susceptible to an
377+
// attacker injecting additional transactions by crafting 64-byte
378+
// transactions matching an inner Merkle node's hash (see
379+
// https://web.archive.org/web/20240329003521/https://bitslog.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/).
380+
// To protect against this (highly unlikely) attack vector, we check that the
381+
// transaction is at least 65 bytes in length.
382+
if tx.total_size() == 64 {
383+
log_error!(
384+
self.logger,
385+
"Skipping transaction {} due to retrieving potentially invalid tx data.",
386+
txid
387+
);
388+
return Ok(None);
389+
}
390+
375391
if let Some(block_height) = known_block_height {
376392
// We can take a shortcut here if a previous call already gave us the height.
377393
return Ok(Some(ConfirmedTx { tx, txid, block_header, pos, block_height }));

0 commit comments

Comments
 (0)