Durable Nonce Manipulation
Detects missing validation when interacting with durable nonce accounts, enabling replay attacks or transaction reordering.
Durable Nonce Manipulation
Overview
The durable nonce manipulation detector identifies Solana programs that interact with durable nonce accounts without implementing proper validation. Durable nonces allow transactions to remain valid beyond the typical blockhash expiry window, but improper handling can enable replay attacks and unauthorized transaction reordering. For remediation steps, see the Durable Nonce Manipulation Remediation.
Why This Is an Issue
Durable nonce accounts on Solana provide an alternative to recent blockhash-based transaction expiry. Instead of expiring after roughly 150 slots, a transaction using a durable nonce remains valid until the nonce is advanced. Programs that interact with nonce accounts must validate:
- Authority: The nonce account authority matches the expected signer. Without this check, any account could be substituted as a nonce, bypassing replay prevention.
- State: The nonce account is initialized and contains a valid nonce value. Using an uninitialized nonce account can lead to undefined behavior.
- Advancement: The nonce value is properly consumed after use, preventing the same nonce from being reused for replay attacks.
Failure to validate these properties allows attackers to replay transactions, reorder operations to their advantage, or manipulate nonce state to bypass time-sensitive logic.
How to Resolve
// Before: Vulnerable -- no authority or state validation
pub fn use_nonce(accounts: &[AccountInfo]) -> ProgramResult {
let nonce_account = &accounts[2];
// Directly uses nonce account without validation
invoke(
&system_instruction::advance_nonce_account(nonce_account.key, nonce_account.key),
&[nonce_account.clone()],
)?;
Ok(())
}
// After: Fixed -- validates authority and state before use
pub fn use_nonce(accounts: &[AccountInfo]) -> ProgramResult {
let nonce_account = &accounts[2];
let expected_authority = &accounts[3];
// Validate nonce account authority
let nonce_data = nonce_account.try_borrow_data()?;
let nonce_state: nonce::state::Data = bincode::deserialize(&nonce_data)
.map_err(|_| ProgramError::InvalidAccountData)?;
require!(
nonce_state.authority == *expected_authority.key,
ProgramError::InvalidArgument
);
invoke(
&system_instruction::advance_nonce_account(nonce_account.key, expected_authority.key),
&[nonce_account.clone(), expected_authority.clone()],
)?;
Ok(())
}
Examples
Vulnerable Code
pub fn process_with_nonce(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let nonce_info = &accounts[0];
// No validation of nonce authority or state
let ix = system_instruction::advance_nonce_account(
nonce_info.key,
nonce_info.key, // Assumes authority is the account itself
);
invoke(&ix, &[nonce_info.clone()])?;
// Process transaction...
Ok(())
}
Fixed Code
pub fn process_with_nonce(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let nonce_info = &accounts[0];
let authority_info = &accounts[1];
// Validate nonce account is owned by System Program
if nonce_info.owner != &system_program::ID {
return Err(ProgramError::InvalidAccountOwner);
}
// Validate authority is a signer
if !authority_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
let ix = system_instruction::advance_nonce_account(
nonce_info.key,
authority_info.key,
);
invoke(&ix, &[nonce_info.clone(), authority_info.clone()])?;
Ok(())
}
Example JSON Finding
{
"detector": "durable-nonce-manipulation",
"severity": "high",
"confidence": 0.78,
"title": "Missing Authority Validation on Nonce Account Operation",
"description": "Durable nonce account operation 'AdvanceNonceAccount' is performed without validating the nonce account authority.",
"cwe_ids": [345]
}
Detection Methodology
- Nonce operation identification: Scans for CPI calls and syscall invocations that match System Program nonce instruction discriminators (InitializeNonceAccount, AdvanceNonceAccount, WithdrawNonceAccount, AuthorizeNonceAccount, UpgradeNonceAccount).
- Authority validation tracking: Identifies patterns where account owner data is loaded and compared, indicating authority validation.
- State validation detection: Searches for branch conditions that check nonce account state before performing operations.
- Gap analysis: Reports nonce operations that lack either authority or state validation, with confidence adjustments for Anchor programs and admin functions.
Limitations
False positives: Programs that validate nonce authority via helper functions or external libraries may be flagged if the validation pattern is not recognized. Admin-gated functions receive reduced confidence. False negatives: Custom nonce implementations that do not use standard System Program instructions will not be detected.
Related Detectors
- Signature Replay — detects missing replay protection
- Missing Signer Check — detects missing authority validation