◂ labs
TIER 1 · THE ACCOUNT MODEL · 1-account-data-matching

ACCOUNT DATA MATCHING

An account that is structurally right but semantically wrong: the type checks out, the data inside belongs to someone else. Without checking a stored field (e.g. `account.authority == signer.key`), the program trusts whatever was passed.

seen in the wild
Variant of the Cashio infinite-mint: an unvalidated collateral account let the attacker mint $52M of CASH.
rust · anchor
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_pack::Pack;
use spl_token::state::Account as SplTokenAccount;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod account_data_matching_insecure {
    use super::*;

    pub fn log_message(ctx: Context<LogMessage>) -> ProgramResult {
        let token = SplTokenAccount::unpack(&ctx.accounts.token.data.borrow())?;
        msg!("Your account balance is: {}", token.amount);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct LogMessage<'info> {
    token: AccountInfo<'info>,
    authority: Signer<'info>,
}