Skip to main content
Sigvex

SPL Token Close Authority Bypass

Detects token account closure without validating the close_authority field.

SPL Token Close Authority Bypass

Overview

Remediation Guide: How to Fix SPL Token Close Authority Bypass

The close authority bypass detector identifies programs that close or burn token accounts without validating the close_authority field. Token accounts have a dedicated close_authority field that specifies who can close the account. If a program does not read and compare this field against the signer, unauthorized parties can close accounts and drain the remaining SOL rent. This vulnerability appears in approximately 15% of recent Solana security audits.

Why This Is an Issue

SPL Token accounts have a close_authority field (an Option<Pubkey>) that designates who can close the account. When set, only that authority should be allowed to close the account. Without checking this field:

  • Unauthorized closure: Attackers close accounts they do not control
  • Rent theft: SOL rent from closed accounts is sent to the attacker’s address
  • Protocol disruption: Closing protocol-owned accounts disrupts operations
  • Token loss: If combined with insufficient balance checks, tokens may be lost

CWE mapping: CWE-863 (Incorrect Authorization).

How to Resolve

pub fn close_account_safe(accounts: &[AccountInfo]) -> ProgramResult {
    let token_account = &accounts[0];
    let destination = &accounts[1];
    let authority = &accounts[2];

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

    // Read and validate close_authority
    let token_data = Account::unpack(&token_account.data.borrow())?;
    if let COption::Some(close_auth) = token_data.close_authority {
        if *authority.key != close_auth {
            return Err(ProgramError::Custom(ErrorCode::InvalidCloseAuthority as u32));
        }
    } else {
        // No close_authority set -- owner can close
        if *authority.key != token_data.owner {
            return Err(ProgramError::InvalidAccountData);
        }
    }

    let ix = spl_token::instruction::close_account(
        &spl_token::id(), token_account.key, destination.key, authority.key, &[],
    )?;
    invoke(&ix, accounts)?;
    Ok(())
}

Examples

Vulnerable Code

pub fn close_account(accounts: &[AccountInfo]) -> ProgramResult {
    let token_account = &accounts[0];
    let destination = &accounts[1];
    let authority = &accounts[2]; // Not checked against close_authority!

    // VULNERABLE: no close_authority validation
    let ix = spl_token::instruction::close_account(
        &spl_token::id(), token_account.key, destination.key, authority.key, &[],
    )?;
    invoke(&ix, accounts)?;
    Ok(())
}

Sample Sigvex Output

{
  "detector_id": "spl-token-close-authority-bypass",
  "severity": "high",
  "confidence": 0.82,
  "description": "Token account closed without validating close_authority field. Unauthorized parties could close accounts and drain rent.",
  "location": { "function": "close_account", "offset": 1 }
}

Detection Methodology

  1. Validation pass: Tracks field loads from token account data (specifically the close_authority field) and comparisons that validate the close authority.
  2. CPI pass: Identifies CloseAccount and Burn CPI operations.
  3. Gap analysis: Reports findings when close/burn operations occur without prior close_authority field access and comparison.

Limitations

  • Close authority field access is detected via heuristic offset analysis; different token account layouts may not be recognized.
  • The SPL Token program itself validates close authority during CPI execution, but program-level checks prevent unauthorized CPI attempts.

References