Skip to main content
Sigvex

Admin Key Management

Detects unsafe admin key handling including hardcoded keys, missing validation, and single points of failure.

Admin Key Management

Overview

Remediation Guide: How to Fix Admin Key Management Issues

The admin key management detector identifies unsafe handling of admin keys in Solana programs. It flags hardcoded admin keys embedded in program bytecode, privileged operations that execute without verifying the caller holds the admin key, and admin functions protected by only a single signer with no multi-signature requirement. These patterns create single points of failure that attackers can exploit to compromise the entire program.

Why This Is an Issue

Admin keys control the most sensitive operations in a Solana program – fund transfers, configuration changes, upgrades, and emergency actions. Unsafe handling leads to several attack vectors:

  • Hardcoded admin keys (CWE-798) are immutable once deployed. If the private key is leaked, the program cannot rotate to a new admin. Attackers can extract hardcoded keys from on-chain bytecode.
  • Missing admin validation (CWE-284) allows any caller to execute privileged operations such as draining funds or modifying program state.
  • Single admin key (CWE-269) with no multi-sig requirement means one compromised key grants full control. There is no separation of duties or recovery mechanism.

How to Resolve

Native Solana

use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};

pub fn process_admin_action(
    accounts: &[AccountInfo],
    program_id: &Pubkey,
) -> Result<(), ProgramError> {
    let admin = &accounts[0];
    let config_account = &accounts[1];

    // 1. Verify signer
    if !admin.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }

    // 2. Load admin key from on-chain config (NOT hardcoded)
    let config_data = config_account.try_borrow_data()?;
    let stored_admin = Pubkey::try_from(&config_data[0..32])
        .map_err(|_| ProgramError::InvalidAccountData)?;

    // 3. Validate caller matches stored admin
    if *admin.key != stored_admin {
        return Err(ProgramError::InvalidArgument);
    }

    // Safe to proceed with admin operation
    Ok(())
}

Anchor

#[derive(Accounts)]
pub struct AdminAction<'info> {
    #[account(
        constraint = admin.key() == config.admin_authority @ ErrorCode::Unauthorized
    )]
    pub admin: Signer<'info>,
    #[account(seeds = [b"config"], bump)]
    pub config: Account<'info, ProgramConfig>,
}

#[account]
pub struct ProgramConfig {
    pub admin_authority: Pubkey,
}

Examples

Vulnerable Code

// VULNERABLE: hardcoded admin key
const ADMIN_KEY: Pubkey = solana_program::pubkey!("HardCodedAdminPubkeyXXXXXXXXXXXXXXXXXXXXXXXX");

pub fn withdraw_all(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let vault = &accounts[0];
    let destination = &accounts[1];

    // No signer check, no admin validation
    **vault.try_borrow_mut_lamports()? -= amount;
    **destination.try_borrow_mut_lamports()? += amount;
    Ok(())
}

Fixed Code

pub fn withdraw_all(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let admin = &accounts[0];
    let vault = &accounts[1];
    let destination = &accounts[2];
    let config = &accounts[3];

    if !admin.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }

    let config_data = config.try_borrow_data()?;
    let stored_admin = Pubkey::try_from(&config_data[0..32])
        .map_err(|_| ProgramError::InvalidAccountData)?;
    if *admin.key != stored_admin {
        return Err(ProgramError::InvalidArgument);
    }

    **vault.try_borrow_mut_lamports()? -= amount;
    **destination.try_borrow_mut_lamports()? += amount;
    Ok(())
}

Sample Sigvex Output

{
  "detector_id": "admin-key-management",
  "severity": "critical",
  "confidence": 0.78,
  "title": "Hardcoded Admin Key",
  "description": "Admin key is hardcoded in the program bytecode. This creates an immutable single point of failure. An attacker with the hardcoded key can compromise the entire program.",
  "location": { "function": "admin_transfer", "block": 0, "statement": 0 },
  "cwe": 798
}

Detection Methodology

The detector performs static analysis on the program’s intermediate representation:

  1. Admin key identification: Variables in the parameter range that represent caller-provided accounts are flagged as potential admin keys.
  2. Hardcoded constant detection: Assignments of constant values to admin-like variables indicate hardcoded keys embedded in bytecode.
  3. Validation tracking: Signer checks, key comparisons, and owner verifications are tracked to determine which accounts have been validated before use.
  4. Privileged operation scanning: State writes and lamport transfers are identified as privileged operations requiring admin validation.
  5. Gap analysis: Privileged operations performed on accounts without prior validation generate findings. Functions with a single verified admin key and an admin-like name trigger a multi-sig warning.
  6. Context adjustment: Confidence is reduced for Anchor programs (where constraints may handle access control) and read-only functions.

Limitations

False positives:

  • Anchor programs where Signer and has_one constraints handle admin validation before the instruction handler executes.
  • Programs using PDA-based authority where the admin key is derived rather than stored directly.

False negatives:

  • Admin keys stored in complex nested account structures that the heuristic does not trace.
  • Multi-instruction transactions where admin validation occurs in a separate instruction.

References