Before Bitcoin Core v25.0, an attacker could remotely crash Bitcoin Core nodes by triggering an assertion in the blocktxn message handling logic.
This issue is considered High severity.
Details
When receiving a block announcement via a cmpctblock message, Bitcoin Core attempts to reconstruct the announced block using the transactions in its own mempool as well as other available transactions. If reconstruction fails due to missing transactions it will request them from the announcing peer via a getblocktxn message. In response a blocktxn message is expected, which should contain the requested transactions.
The compact block protocol employs shortened transaction identifiers to reduce bandwidth. These short-ids are 6 byte in size, resulting in a small chance for collisions (i.e. transaction A has the same short-id as transaction B) upon block reconstruction. Collisions will be detected as the merkle root computed from the reconstructed set of transactions will not match the merkle root from the block announcement. Peers should not be punished for collisions as they may happen spuriously, therefore they are handled by falling back to requesting the full block.
Bitcoin Core will create an instance of PartiallyDownloadedBlock
whenever a new compact block is received. If missing transactions are
requested, the instance is persisted until the corresponding blocktxn message
is processed. Upon receiving the blocktxn message,
PartiallyDownloadedBlock::FillBlock
is called, attempting to
reconstruct the full block. In the collision case described above, the full
block is requested but the PartiallyDownloadedBlock
instance as
well as the other state related to the underlying block request is left
untouched. This leaves room for a second blocktxn message for the same block to
be processed and trigger FillBlock
to be called again. This
violates the assumption (documented as an assert
statement) that
FillBlock
can only be called once and causes the node to crash.
An attacker does not need to get lucky by triggering a collision, as the collision handling logic can easily be triggered by simply including transactions in the blocktxn message that are not committed to in the block’s merkle root.
Attribution
Credit goes to Niklas Gögge for discovering and disclosing the vulnerability, as well as fixing the issue in https://github.com/bitcoin/bitcoin/pull/26898.
Timeline
- 2022-10-05 - Niklas Gögge reports the issue to the Bitcoin Core security mailing list.
- 2023-01-24 - PR #26898 containing the fix is merged.
- 2023-05-25 - Bitcoin Core 25.0 is released with the fix.
- 2024-10-09 - Public disclosure.