Cashio
Summary
ExpandThe codebase contains a critical access control vulnerability in the collateral validation logic. The `BrrrCommon` validation checks only enforce *internal consistency* between the `bank`, `collateral`, `crate_mint`, and Saber swap accounts, but never verify that the `bank` account is the protocol's authorized bank. This allows an attacker to create a fake bank pointing to the real $CASH crate mint, register arbitrary collateral under it, and mint unlimited $CASH backed by worthless tokens.
Both `print_cash` and `burn_cash` are currently disabled via `vipers::invariant!(false, "temporarily disabled")`, which prevents immediate exploitation. However, the underlying vulnerability persists in the program logic and would be exploitable the moment the invariant is removed. A minor issue with missing event emission in `burn_cash` was also identified.
Findings
2 issues identified
Missing Bank Origin Validation Allows Unlimited $CASH Minting
programs/brrr/src/actions/mod.rs:11
View Details
Description
BrrrCommon::validate() checks internal consistency between bank, collateral, crate_mint, and Saber swap accounts, but never verifies that the bank is the protocol’s authorized bank. An attacker can create a fake Bank via bankman with crate_mint set to the real $CASH mint, register worthless collateral under it, and mint unlimited $CASH. All validation checks pass because they only verify that the provided accounts are consistent with each other, not that they are the correct accounts for the protocol.
Impact
An attacker can mint unlimited $CASH backed by zero-value collateral, then redeem it for legitimate LP tokens, draining all protocol funds. This is the exact pattern that led to the real-world Cashio exploit (~$52M loss).
Recommendation
Add an explicit check that the bank account matches a known, hardcoded protocol bank address. Add a BANK_ADDRESS constant in addresses.rs and validate with assert_keys_eq!(self.bank.key(), BANK_ADDRESS) as the first check in BrrrCommon::validate().
Fix
Adding assert_keys_eq!(self.bank, BANK_ADDRESS) ensures only the protocol's authorized bank can be used, preventing attackers from passing a fake bank with fake collateral. BANK_ADDRESS must be defined as a constant in addresses.rs with the actual deployed bank pubkey.
programs/brrr/src/actions/mod.rs
Missing Bank Address Validation Allows Unlimited $CASH Minting
programs/brrr/src/actions/mod.rs:10
View Details
Description
BrrrCommon::validate() checks internal consistency between bank, collateral, crate_mint, and saber_swap accounts, but never verifies that the bank account is the expected legitimate bank. The validation is entirely self-referential: any Bank/Collateral pair created via the bankman program that references itself correctly will pass all assert_keys_eq! checks. An attacker can create a fake Bank with the real CASH crate_mint, add a fake Collateral entry, provide manipulated Saber swap accounts, and call print_cash to mint unlimited $CASH. This is the exact vulnerability exploited in the March 2022 Cashio hack (~$48M stolen).
Impact
Complete protocol compromise. Attacker can mint unlimited $CASH backed by worthless collateral, then burn the $CASH to drain all legitimate collateral (Saber LP tokens) held by the crate. Total loss of all user-deposited collateral.
Recommendation
Add a hardcoded bank address constant and validate the bank account against it in BrrrCommon::validate(), consistent with the existing ISSUE_AUTHORITY_ADDRESS and WITHDRAW_AUTHORITY_ADDRESS patterns.
Fix
Adds a check that the bank account matches the known legitimate BANK_ADDRESS before any other validation. This prevents attackers from substituting a fake self-consistent Bank/Collateral pair. A corresponding BANK_ADDRESS constant must be added to addresses.rs using static_pubkey! with the legitimate bank's public key.
programs/brrr/src/actions/mod.rs
Conclusion
ExpandThe Cashio brrr program contains a critical vulnerability in its collateral validation logic: the Bank account is never verified to be the protocol’s authorized bank. All validation checks enforce internal consistency between user-supplied accounts, but an attacker who creates their own internally-consistent set of accounts (fake bank, fake collateral, worthless Saber pool) can mint unlimited $CASH. This is the exact vulnerability pattern that led to the real-world $52M Cashio exploit in March 2022.
The functions are currently disabled via invariant!(false), which prevents immediate exploitation but does not address the root cause. Before re-enabling minting or burning, the bank address must be explicitly validated against a known authorized value. The fix is straightforward — a single assert_keys_eq! check against a hardcoded bank address — and should be applied before any consideration of re-enabling the protocol.
Legal Disclaimer: This report covers the code submitted for analysis. It does not account for infrastructure, deployment configuration, third-party dependencies, or changes made after the audit date. Automated analysis may produce false positives or miss context-dependent vulnerabilities. audited.xyz provides this report “as is” without warranty of any kind.