Weak PDA Entropy
Detects PDA derivations with weak seed entropy, enabling prediction and front-running attacks.
Weak PDA Entropy
Overview
Remediation Guide: How to Fix Weak PDA Entropy
The weak PDA entropy detector identifies PDA derivations using predictable seed sources: sequential numeric IDs, timestamps (only 2^32 values per second), low-entropy numeric ranges (0-999), and hardcoded predictable values. Weak seed entropy allows attackers to pre-compute PDA addresses, enumerate all possible accounts, and front-run transactions.
The detector reports three specific patterns:
- Timestamp seeds: Clock-derived values with very low entropy (High severity).
- Low-entropy numeric seeds: Small constant values that are easily enumerable (Medium severity).
- Sequential numeric IDs: Cast variables likely representing counters or indices (Medium severity).
Why This Is an Issue
PDAs with predictable seeds allow attackers to enumerate all possible account addresses offline. An attacker who knows the seed pattern can pre-create accounts at target PDA addresses before legitimate users, front-run transactions by predicting upcoming PDA addresses, or target specific users by computing their PDA addresses from public information.
CWE mapping: CWE-330 (Use of Insufficiently Random Values).
How to Resolve
Native Solana
// VULNERABLE: timestamp seed
let ts = Clock::get()?.unix_timestamp;
let (pda, _) = find_program_address(&[b"session", &ts.to_le_bytes()], program_id);
// FIXED: use public key (32 bytes of entropy)
let (pda, _) = find_program_address(&[b"session", user.key.as_ref()], program_id);
Anchor
#[account(seeds = [b"vault", user.key().as_ref()], bump)]
pub vault: Account<'info, Vault>,
Examples
Vulnerable Code
pub fn create_pool(accounts: &[AccountInfo], pool_index: u8) -> ProgramResult {
// Low-entropy: only 256 possible values
let (pool_pda, _) = Pubkey::find_program_address(
&[b"pool", &[pool_index]],
program_id,
);
Ok(())
}
Fixed Code
pub fn create_pool(accounts: &[AccountInfo], pool_index: u8) -> ProgramResult {
// Combine low-entropy index with high-entropy authority key
let (pool_pda, _) = Pubkey::find_program_address(
&[b"pool", authority.key.as_ref(), &[pool_index]],
program_id,
);
Ok(())
}
Sample Sigvex Output
{
"detector_id": "weak-pda-entropy",
"severity": "high",
"confidence": 0.78,
"description": "PDA derivation uses a timestamp value as a seed component. Timestamps provide very low entropy and are easily predictable.",
"location": { "function": "create_session", "block": 0, "stmt": 0 }
}
Detection Methodology
- PDA syscall detection: Identifies
find_program_addressandcreate_program_addresscalls. - Seed entropy analysis: Recursively analyzes seed expressions for weak sources:
- Clock/timestamp syscalls (high confidence)
- Small constants under 1000 (medium confidence)
- Cast variables suggesting sequential IDs (medium confidence)
- Nested expression traversal: Follows binary operations and casts to find weak entropy sources in compound expressions.
Limitations
- Variables representing public keys (high entropy) cannot be distinguished from sequential counters without type information.
- The sequential ID heuristic (detecting cast variables) may produce false positives for legitimate type conversions.
- Seeds loaded from memory or computed by helper functions are not analyzed for entropy.
Related Detectors
- PDA System Collision — detects weak seeds that could collide with system addresses
- PDA Seed Validation — validates overall PDA seed patterns