Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-46369

CVE-2026-46369: Nimiq Proof-of-Stake RCE Vulnerability

CVE-2026-46369 is a remote code execution flaw in Nimiq Proof-of-Stake protocol that allows transaction replay attacks leading to duplicate balance updates. This post explains its impact, affected versions, and mitigation steps.

Updated:

CVE-2026-46369 Overview

CVE-2026-46369 affects Nimiq, a Rust implementation of the Nimiq Proof-of-Stake protocol based on the Albatross consensus algorithm. Versions through 1.5.0 contain an off-by-one error [CWE-193] in the validity store, which uses a strict lower-bound comparison that expires stored transactions earlier than Transaction::is_valid_at permits. A remote attacker can replay the same signed transaction during a blocks_per_batch minus one block window. The replay causes both sender and recipient balances to be updated twice, corrupting on-chain state. The issue is fixed in version 1.5.1.

Critical Impact

Remote, unauthenticated attackers can replay signed transactions within a specific block window, causing duplicate balance updates on Nimiq Albatross chains running versions through 1.5.0.

Affected Products

  • Nimiq core-rs-albatross versions up to and including 1.5.0
  • Nimiq Proof-of-Stake nodes using the Albatross consensus algorithm
  • Fixed in core-rs-albatross version 1.5.1

Discovery Timeline

  • 2026-08-26 - CVE-2026-46369 published to NVD
  • 2026-08-26 - Last updated in NVD database

Technical Details for CVE-2026-46369

Vulnerability Analysis

The vulnerability resides in the Nimiq history store's validity window enforcement logic. Nimiq nodes maintain a validity store that tracks recent transactions to prevent replay. The store used a strict lower-bound comparison to decide when a transaction hash could be evicted from replay protection. That comparison expired stored transaction records one batch earlier than the protocol-level Transaction::is_valid_at function considered the transaction still valid.

During the mismatch window, the same signed transaction remained acceptable to block validation while no longer being recognized as previously included. A remote peer can therefore resubmit the identical signed transaction, and validators will process it as new. Each replay debits the sender and credits the recipient again, breaking accounting invariants and enabling economic abuse against exchanges, contracts, and end users.

Root Cause

The root cause is an off-by-one boundary condition [CWE-193] between two independent checks: the validity store's eviction predicate and the transaction's own is_valid_at predicate. The store's strict lower-bound comparison used blocks_per_batch rather than the full transaction_validity_window_blocks reference point, opening a blocks_per_batch minus one block gap where replay was possible.

Attack Vector

The attack requires only network access and no authentication or user interaction. An attacker observes a confirmed transaction, waits for the validity store to prematurely drop its hash, and then rebroadcasts the original signed transaction to the network. Validators re-execute it, applying the balance transfer a second time.

rust
// Patched test asserts protocol-aligned validity window boundaries
#[test]
fn transaction_in_validity_window_matches_protocol_validity() {
    let env = MdbxDatabase::new_volatile(Default::default()).unwrap();
    let history_store = HistoryStore::new(env.clone(), NetworkId::UnitAlbatross);

    let first_inclusion_block = Policy::genesis_block_number() + 1;
    let validity_start_height = first_inclusion_block + Policy::blocks_per_batch();
    let last_valid_block =
        validity_start_height + Policy::transaction_validity_window_blocks() - 1;

    let replay_tx = create_transaction_with_validity_start(
        first_inclusion_block, 0, validity_start_height,
    );
    let raw_tx = match &replay_tx.data {
        HistoricTransactionData::Basic(tx) => tx.get_raw_transaction(),
        _ => unreachable!("test helper must create a basic transaction"),
    };

    assert!(raw_tx.is_valid_at(first_inclusion_block));
    assert!(raw_tx.is_valid_at(last_valid_block));
    assert!(!raw_tx.is_valid_at(last_valid_block + 1));
}
// Source: https://github.com/nimiq/core-rs-albatross/commit/a530b2434ebca6e3716f07c73079786fcc6f2e41

The patch aligns the validity store's replay window with Transaction::is_valid_at, closing the off-by-one gap. Full context is available in the GitHub commit and the GHSA-3763-qp59-59vf advisory.

Detection Methods for CVE-2026-46369

Indicators of Compromise

  • Duplicate transaction hashes appearing in confirmed blocks within a blocks_per_batch minus one block interval.
  • Sender or recipient account balances changing by the same signed transaction amount more than once.
  • Peers rebroadcasting previously confirmed transactions after the validity store eviction boundary.

Detection Strategies

  • Compare HistoryStore inclusion records against ledger balance deltas to detect duplicated transaction application.
  • Instrument nodes to log the block height at which a transaction hash exits the validity store, and alert when the same signed transaction is accepted twice.
  • Reconcile exchange and wallet accounting against on-chain state to identify unexpected duplicate credits.

Monitoring Recommendations

  • Track Nimiq node versions across the fleet and flag any node running core-rs-albatross at or below 1.5.0.
  • Monitor mempool ingestion for transactions whose hashes match previously finalized transactions.
  • Alert on abnormal spikes in identical transaction payloads originating from the same peer.

How to Mitigate CVE-2026-46369

Immediate Actions Required

  • Upgrade all Nimiq core-rs-albatross nodes, validators, and RPC endpoints to version 1.5.1 or later.
  • Audit recent chain history for duplicate transaction inclusions within any blocks_per_batch minus one block window.
  • Coordinate with exchanges and custodial services to reconcile balances that may reflect duplicated credits.

Patch Information

The fix is included in core-rs-albatross version 1.5.1. The patch corrects the validity store's boundary comparison so the replay-protection window matches Transaction::is_valid_at. See the upstream commit and the pull request discussion for implementation details.

Workarounds

  • No supported workaround exists short of upgrading; operators should not run 1.5.0 or earlier in production.
  • Where immediate upgrade is impossible, restrict peer connectivity to trusted nodes and increase mempool duplicate-hash validation as a temporary defensive layer.
bash
# Upgrade Nimiq core-rs-albatross to the patched release
git clone https://github.com/nimiq/core-rs-albatross.git
cd core-rs-albatross
git checkout v1.5.1
cargo build --release
# Restart the validator/node service after replacing the binary
systemctl restart nimiq-node

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.