Security Audit of Solana Smart Contracts

Mo Ashouri
2 min readMay 6, 2022

Solana smart contracts can be written in Rust or even C. Solana presents a distinct smart contract model to traditional EVM-based blockchains. A smart contract is read-only or stateless and retains only program logic. Once deployed, smart contracts can be interacted with by external accounts.

Security audits for Solana smart contracts can be conducted by manual code review or using auditing tools and reviewing the logic and execution of the smart contract.

THINK LIKE A HACKER!

A concrete auditor must have a mindset of hackers and understand their incentives. For example, a hacker thinks:

  • How can I steal tokens from the smart contract?
  • How can I freeze the contract, such as lock user funds, disable the depositing/withdrawing process, or disable the upgrade?
  • How can I make the smart contract to send money to the wrong user?
  • How can I change the smart contract’s critical states, such as changing the owner or the validator list?
  • How can I infect the smart contract’s code?
  • How can I claim more refunds than deserved?
  • How can I buy more tokens than permitted?

This list can easily get extended to numerous questions and scenarios.

IDENTIFY POTENTIAL ATTACK SURFACES

Solana contracts have a single entry point defined by the entrypoint! macro, wheres in Solidity every public or external function can be invoked by hackers!

entrypoint!(processX);fn processX(
program_id: &Pubkey,
accounts: &[AccountInfo],
_data: &[u8],
) -> ProgramResult

In the above code, the function processX is the single entry point, which takes three parameters: program_id , accounts , and _data.
This function in the contract can implement any business logic.
Note that hackers can also control accounts and _data. Consequently, hackers can inject unexpected data for accounts and _data in order to exploit a Solana smart contract specified by program_id.

Now look at the following example:

pub fn processX(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
let instruction = DeFiInstruction::try_from_slice(input)?;
match instruction {
DeFiInstruction::Initialize {…} => _initialize(…),
DeFiInstruction::Deposit {…} => _deposit(…),
DeFiInstruction::StakeDeposit { … } => _stake_deposit(…),
...
}

Based on the results of instruction_data parsing, the contract then calls various functions to process separate logics, namely _initialize , _deposit , and _stake_deposit. These functions can write or read data in accounts, call helper functions, call Rust library or even call other on-chain programs. As a result, a security issue in any of these paths allows hackers to exploit the contract to steal tokens or other nasty things!

--

--

Mo Ashouri

Mo has a Ph.D. in Compter Science. Mo specializes in Backend Programming and Security.