Skip to main content
Sigvex

Bump Seed Brute Force Remediation

How to fix user-controlled bump seeds in PDA derivation.

Bump Seed Brute Force Remediation

Overview

Related Detector: Bump Seed Brute Force

Bump seed brute force vulnerabilities allow attackers to supply non-canonical PDA bumps, creating alternative valid PDAs that point to attacker-controlled accounts. The fix is to always derive and validate the canonical bump using find_program_address.

Before (Vulnerable)

let bump = instruction_data[32]; // User-controlled
let pda = create_program_address(&[b"vault", user, &[bump]], pid)?;

After (Fixed)

let (pda, canonical_bump) = find_program_address(&[b"vault", user], pid);
// Use canonical_bump, never accept bump from user input

Alternative Mitigations

1. Validate user bump against canonical

If you must accept bumps from input (e.g., for gas optimization with stored bumps):

let user_bump = instruction_data[32];
let (_, canonical_bump) = find_program_address(&[b"vault", user], pid);

if user_bump != canonical_bump {
    return Err(ProgramError::InvalidSeeds);
}

let pda = create_program_address(&[b"vault", user, &[user_bump]], pid)?;

2. Store canonical bump at initialization

// During init: derive and store canonical bump
let (pda, bump) = find_program_address(&[b"vault", user], pid);
state.bump = bump;

// During use: load stored canonical bump
let stored_bump = state.bump;
let pda = create_program_address(&[b"vault", user, &[stored_bump]], pid)?;

3. Anchor bump storage

#[account(
    init,
    seeds = [b"vault", user.key().as_ref()],
    bump,
    payer = user,
)]
pub vault: Account<'info, Vault>,

// Vault struct stores the bump
#[account]
pub struct Vault {
    pub bump: u8,
    // ...
}

Common Mistakes

Mistake 1: Accepting bump from instruction data without validation

// WRONG: no validation against canonical
let bump = data[0];
let pda = create_program_address(&seeds_with_bump, pid)?;

Mistake 2: Using find_program_address only for the address, ignoring the bump

let (expected_pda, _) = find_program_address(&seeds, pid); // Bump discarded!
let user_bump = data[0];
let pda = create_program_address(&[..seeds, &[user_bump]], pid)?;
// Even if pda == expected_pda check passes, non-canonical bumps may exist

Mistake 3: Storing a user-provided bump

// WRONG: storing unvalidated bump
state.bump = instruction_data[32]; // Could be non-canonical

Always derive the canonical bump before storing.

References