Solana is exploding in popularity due to its rock-bottom transaction fees. With more and more high-value Dapps being built on Solana, it is critical to develop auditing techniques for them.
However, auditing Solana smart contracts entail new and more advanced skills compared to Ethereum (which has established auditing rules in the last three years). Solana differs from Ethereum in two major ways:
- it uses a different language — Rust (vs Solidity in Ethereum) and
- it decouples code and data.
In this article series, we will introduce a systematic approach including a few automated techniques for auditing Solana smart contracts.
What attackers want and what they may do?
First of all, an audit should develop a mindset of attackers and understand their incentives. Here are a few user stories from an attacker:
- Can I steal money (SPL tokens, SOL, or other native currencies) from the smart contract?
- Can I freeze the smart contract (lock user funds, disable the depositing/withdrawing process, disable the upgrade, etc)?
- Can I make the smart contract to send money to a wrong user?
- Can I change the smart contract’s critical states (changing the owner, the multisig owner, or the validator list)?
- Can I change the smart contract’s code (upgrading it to a malicious one)?
- Can I buy tokens at a low price?
- Can I claim more refunds than deserved?
- Can I buy more tokens than allowed?
The above list can be easily extended to include attacks that exploit any economic or logic bugs in the smart contracts.
What are the attack surfaces in the smart contracts?
Then, figure out all the attack surfaces of a smart contract. Unlike Solidity in which every public or external function can be called by an attacker, Solana smart contracts have a single entry point defined by the entrypoint! macro:
In the above, the function process_instruction is the single entry point. It passes three parameters: program_id , accounts , and instruction_data , the latter two of which can be controlled by attackers (except program_id). In other words:
Attackers can supply arbitrary data for accounts and instruction_data to exploit a Solana smart contract identified by program_id
In process_instruction , the contract can implement any business logics. The following shows a code snippet from solido:
According to the parsing results from instruction_data, the contract then calls different functions to process different logics: process_initialize , process_deposit , process_stake_deposit , etc. These functions can read and write data in accounts , call Rust library and Solana helper functions, and/or further invoke other on-chain programs. If any of these code paths have a flaw, an attacker may leverage them to steal money or do other bad things.
What are the common vulnerabilities attackers can exploit in Solana?
Then, understand the domain-specific rules and intricacies in Solana. While the number of smart contracts vulnerabilities and attack vectors is growing, there are multiple known vulnerabilities, some are common in all smart contracts, and some are unique in Solana:
- Missing signer checks: if an instruction should only be available to a restricted set of entities, but the program does not verify that the call has been signed by the appropriate entity (e.g., by checking AccountInfo::is_signer ).
- Missing ownership checks: for accounts that are not supposed to be fully user-controlled, the program does not check the AccountInfo::owner field.
- Missing rent exemption checks: all Solana accounts holding an Account, Mint, or Multisig must contain enough SOL to be considered rent exempt. Otherwise the accounts may fail to load.
- Signed invocation of unverified programs: the program does not verify the pubkey of any program called via the invoke_signed() API.
- Solana account confusions: the program fails to ensure that the account data has the type it expects to have.
- Re-initiation with cross-instance confusion
- Arithmetic overflow/underflows: If an arithmetic operation results in a higher or lower value, the value will wrap around with two’s complement.
- Numerical precision errors: numeric calculations on floating point can cause precision errors and those errors can accumulate.
- Loss of precision in calculation: numeric calculations on integer types such as division can loss precision.
- Incorrect calculation: for example, incorrect numerical computes due to copy/paste errors
- Casting truncation
- Exponential complexity in calculation
- Missing freeze authority checks
- Insufficient SPL-Token account verification
- Over/under payment of loans
What are the general issues in Solana and Rust programs?
An audit should also keep in mind that the Solana blockchain and the Rust programming language have several general issues such as depth of cross-program invocations, limited reentrancy, unsafe Rust, and so on.
- Depth of Solana cross-program invocation: the depth is constrained currently to 4.
- Reentrancy: Solana allows self-recursion capped at a fixed depth, due to the depth restriction of cross-program invocation. This prevents most of the Reentrance attacks found in Ethereum.
- Unsafe Rust code: the Rust type system does not check memory safety of unsafe Rust code. Thus, if a smart contract contains any unsafe Rust code, it may still suffer from memory corruptions such as buffer overflows, use after frees, uninitialized memory, etc.
- Outdated dependencies: Rust/Cargo makes it easy to manage dependencies, but the dependencies can be outdated or contain known security vulnerabilities. cargo-outdated can be used to check outdated dependencies.
- Redundant code: repeated code or dead code that can be cleaned or simplified to reduce code complexity.
- Do not follow security best practices: failing to properly use assertions, check user errors, multisig, and so on.
Checking high-level logic and economic errors
Besides inspecting these common vulnerabilities and general issues, an audit should also check the smart contract against high-level semantic errors, such as:
- Ensuring that the contract logic correctly implements the project specifications,
- Examining the code in detail for contract-specific low-level vulnerabilities,
- Ruling out economic attacks,
- Ruling out denial of service attacks
- Checking for instructions that allow front-running or sandwiching attacks,
- Checking for unsafe design which might lead to common vulnerabilities being introduced in the future,
- Checking for any other, as-of-yet unknown classes of vulnerabilities arising from the structure of the Solana blockchain,
- Checking for rug-pull mechanisms or hidden backdoors.
The Soteria team is pleased to provide audit services to high-impact Dapps on Solana. Email firstname.lastname@example.org or visit soteria.dev
Soteria is founded by leading minds in the fields of blockchain security and software verification.
In the next few articles, we will elaborate on some of the common vulnerabilities with real contract examples, on auditing Solana smart contracts written with the Anchor framework, and will also introduce automated security scanning and testing tools.
How to audit Solana smart contracts series?
For all blogs by Soteria, Please visit https://www.soteria.dev/blogs