Skip to content

Commit 77f8af0

Browse files
authored
Merge pull request #3215 from tnull/2024-08-protect-against-merkle-leaf-node-weakness
tx-sync: Protect against Core's Merkle leaf node weakness
2 parents c1c133c + 44a479e commit 77f8af0

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)