Skip to main content
Sigvex

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:

  1. Timestamp seeds: Clock-derived values with very low entropy (High severity).
  2. Low-entropy numeric seeds: Small constant values that are easily enumerable (Medium severity).
  3. 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

  1. PDA syscall detection: Identifies find_program_address and create_program_address calls.
  2. 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)
  3. 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.

References