Mint Authority Abuse
Detects token minting operations without proper mint authority validation, enabling unauthorized token supply inflation.
Mint Authority Abuse
Overview
Remediation Guide: How to Fix Mint Authority Abuse
The mint authority abuse detector identifies Solana programs that invoke SPL Token mint_to or mint_to_checked instructions without validating the mint authority account. When a program performs a CPI to the Token program to mint tokens but does not verify that the provided authority account is the legitimate mint authority, an attacker can supply their own account as the authority and mint unlimited tokens.
The detector uses CFG-aware dataflow analysis to track whether authority accounts have been validated (via signer checks, owner checks, or key checks) before reaching a mint CPI.
Why This Is an Issue
SPL Token minting requires the mint authority’s signature. If a program constructs a mint_to CPI and passes an unvalidated account as the authority:
- An attacker provides their own keypair as the mint authority account
- If the program does not check that this account matches the mint’s actual authority, the CPI may succeed with the attacker’s signature
- The attacker mints arbitrary token amounts, inflating supply
- Token holders suffer dilution, and the attacker can sell minted tokens for profit
This has been a recurring vulnerability pattern in Solana token programs and launchpad contracts.
CWE mapping: CWE-285 (Improper Authorization), CWE-269 (Improper Privilege Management).
How to Resolve
// Before: Vulnerable -- mint authority not validated
pub fn mint_tokens(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let destination = &accounts[1];
let authority = &accounts[2]; // Not validated
let token_program = &accounts[3];
let ix = spl_token::instruction::mint_to(
token_program.key,
mint.key,
destination.key,
authority.key, // Could be attacker's key
&[],
amount,
)?;
invoke(&ix, accounts)?;
Ok(())
}
// After: Validate mint authority before CPI
pub fn mint_tokens(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let destination = &accounts[1];
let authority = &accounts[2];
let token_program = &accounts[3];
// FIXED: verify authority is signer
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// FIXED: verify authority matches the mint's mint_authority
let mint_data = mint.try_borrow_data()?;
let mint_state = spl_token::state::Mint::unpack(&mint_data)?;
if mint_state.mint_authority.unwrap() != *authority.key {
return Err(ProgramError::InvalidAccountData);
}
let ix = spl_token::instruction::mint_to(
token_program.key,
mint.key,
destination.key,
authority.key,
&[],
amount,
)?;
invoke(&ix, accounts)?;
Ok(())
}
Examples
Vulnerable Code
pub fn airdrop(accounts: &[AccountInfo], recipients: &[Pubkey], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let authority = &accounts[1];
let token_program = &accounts[2];
// No check that authority == mint.mint_authority
// No check that authority.is_signer
for (i, recipient) in recipients.iter().enumerate() {
let dest = &accounts[3 + i];
let ix = spl_token::instruction::mint_to(
token_program.key, mint.key, dest.key,
authority.key, &[], amount,
)?;
invoke(&ix, accounts)?;
}
Ok(())
}
Fixed Code
pub fn airdrop(accounts: &[AccountInfo], recipients: &[Pubkey], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let authority = &accounts[1];
let token_program = &accounts[2];
// FIXED: validate signer
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// FIXED: validate authority matches mint
let mint_data = mint.try_borrow_data()?;
let mint_state = spl_token::state::Mint::unpack(&mint_data)?;
if mint_state.mint_authority.unwrap() != *authority.key {
return Err(ProgramError::InvalidAccountData);
}
// FIXED: validate token program
if token_program.key != &spl_token::id() {
return Err(ProgramError::IncorrectProgramId);
}
for (i, _recipient) in recipients.iter().enumerate() {
let dest = &accounts[3 + i];
let ix = spl_token::instruction::mint_to(
token_program.key, mint.key, dest.key,
authority.key, &[], amount,
)?;
invoke(&ix, accounts)?;
}
Ok(())
}
Sample Sigvex Output
{
"detector_id": "mint-authority-abuse",
"severity": "critical",
"confidence": 0.85,
"description": "A token mint operation was detected without proper mint authority validation. An attacker could potentially mint unlimited tokens by providing a fake or compromised mint authority account.",
"location": { "function": "airdrop", "offset": 4 }
}
Detection Methodology
The detector performs CFG-aware dataflow analysis:
- CPI identification: Scans for
InvokeCpistatements that match mint instruction patterns (Token program CPI with mint-related account structures). - Authority extraction: Identifies the authority variable from the CPI accounts list.
- Validation tracking: Uses
SharedDataflowStateto propagate signer checks, owner checks, and key checks across basic blocks. Tracks variable aliases so thatlet auth = accounts[2]followed by a check onauthis recognized. - Vulnerability decision: If the authority variable reaches the mint CPI without any validation in a dominating block, the finding is emitted at Critical severity.
- Blackboard integration: When available, reads pre-computed dataflow state from the analysis blackboard to avoid redundant computation.
Limitations
False positives:
- Programs that validate mint authority in a separate instruction (multi-instruction transaction pattern) may be flagged since cross-instruction validation is not tracked.
- PDA-based mint authorities where the PDA derivation happens in a separate function may not be recognized.
False negatives:
- Indirect mint operations through wrapper programs that obscure the
mint_toCPI pattern. - Programs that validate authority through a governance vote stored in account state.
Related Detectors
- Missing Signer Check — missing signer validation for authority accounts
- Arbitrary CPI — unvalidated token program ID enables mint redirection
- Missing Owner Check — missing ownership validation on mint accounts