Sec3 logo — Solana smart contract security firm
Back to Blog
Tools

X-Ray: A Vulnerability Scanner for Solana Smart Contracts

Sec3 Research Team

Solana is a fast-growing blockchain with a unique type of smart contracts — called Solana programs. This article introduces X-Ray, a security tool that automatically scans Solana programs to detect common security pitfalls.

Common pitfalls in Solana smart contracts

Neodyme recently collected a list of common pitfalls in Solana smart contracts, falling into five categories:

  • Missing ownership check
  • Missing signer check
  • Solana account confusions
  • Arbitrary signed program invocation
  • Integer overflow & underflow

As an example, the code below illustrates a common pitfall of missing signer check.

fn update_admin(accounts: &[AccountInfo], admin: [u8; 32]) -> ProgramResult {
        let acc_iter = &mut accounts.iter();
        let admin_info = next_account_info(acc_iter)?;
        let staking_info = next_account_info(acc_iter)?;
        
        // if !admin_info.is_signer {
        //     return Err(ProgramError::MissingRequiredSignature);
        // }
        
        let mut staking = StakingInfo::try_from_slice(&staking_info.data.borrow())?;
        if staking.admin == [0; 32] {
              staking.admin = admin;
        } else if staking.admin == admin_info.key.to_bytes() {
              staking.admin = admin;
        } else {
              return Err(StakeError::AdminRequired.into());
        }
        
        let _ = staking.serialize(&mut &mut staking_info.data.borrow_mut()[..]);
        Ok(())
}

The function update_admin updates the admin of a staking_info account. It attempts to ensure that the function is only callable by the current admin of staking_info by comparing staking.admin to admin_info account’s public key. It must check that admin_info has actually signed this operation. This can be done by adding the check if _!admin_info.is_signer_. Otherwise if the check is missed, an attacker can update the admin to any account.

The reason is that, in Solana, users can supply arbitrary accounts when invoking a smart contract, so there’s nothing stopping a malicious user from just supplying a fakeadmin_info with admin_info.key.to_bytes()as staking.admin and their own account as the new admin.

X-Ray — Detecting common pitfalls

Powered by the GreenCore technology, sec3 X-Ray can automatically detect security vulnerabilities in Solana programs by checking all code paths against these common pitfalls. The basic idea is to look at the data flow of each user account supplied to the program and flag it as untrustful if its validity is not properly checked in the program’s execution context. Figure 1 shows a screenshot of the missing signer check detected by X-Ray in the update_admin code. Figure 2 shows a screenshot of an arithmetic overflow/underflow detected by X-Ray in the Jet protocol.

Figure 1. A screenshot of missing-signer-checker vulnerabilities found by Soteria

==============VULNERABLE: MissingSignerCheck!==============
Found a potential vulnerability. The account info is missing signer check:

    35|     if staking.admin == [0; 32] {
    36|         staking.admin = admin;
   >37|     } else if staking.admin == admin_info.key.to_bytes() {
    38|         staking.admin = admin;
    39|     } else {
>>>Stack Trace:
>>>entrypoint
>>>    helloworld0::entrypoint::process_instruction::h12125055b6aa6121 [/root/.cargo/registry/src/g
>>>    helloworld0::processor::Processor::process::h6c3f74ddc1014140  [src/entrypoint.rs:14]
>>>    helloworld0::processor::Processor::update_admin::h1e242ce647beea3e [src/processor.rs:19]

Attacker may modify account data at /git/solana-sample/src/processor.rs@36:13
    34|
    35|     if staking.admin == [0; 32] {
   >36|         staking.admin = admin;
    37|     } else if staking.admin == admin_info.key.to_bytes() {
    38|         staking.admin = admin;

Attacker may modify account data at /git/solana-sample/src/processor.rs@38:13
    36|         staking.admin = admin;
    37|     } else if staking.admin == admin_info.key.to_bytes() {
   >38|         staking.admin = admin;
    39|     } else {
    40|         return Err(StakeError::AdminRequired.into());

Figure 1. A screenshot of missing-signer-checker vulnerabilities found by Soteria

Figure 2. A real vulnerability found by X-Ray in the Jet protocol and and fixed by their developers

Timeline of the Jet Protocol vulnerability discovered by X-Ray: On 09/13/2021, the initial version of the Jet Protocol (https://github.com/jet-lab/jet-v1) contained an arithmetic underflow vulnerability in the withdraw function where state.total_deposits -= token_amount and state.total_deposit_notes -= note_amount could underflow if controlled by an untrusted user. On 09/29/2021, the bug was reported. On 10/01/2021, the developers fixed it by replacing the unsafe subtraction with checked_sub(...).unwrap():

// Before (vulnerable):
state.total_deposits -= token_amount;
state.total_deposit_notes -= note_amount;

// After (fixed):
state.total_deposits = state.total_deposits.checked_sub(token_amount).unwrap();
state.total_deposit_notes = state.total_deposit_notes.checked_sub(note_amount).unwrap();

Figure 2. A real vulnerability found by X-Ray in the Jet protocol and and fixed by their developers

How to use X-Ray

Under Solana program’s directory (where Xargo.toml exists), invoke X-Ray . or X-Ray -analyzeAll .

The dot . is a shortcut for the following cargo build command:

Depending on the code complexity, X-Ray currently adds only a second or so to the build time. At the end of the screen, it also shows a summary of the findings and generates a report that can be inspected in the browser.

--------The summary of potential vulnerabilities in token_vesting.ll--------

        4 untrustful account issues
        1 unsafe arithmetic issues
==============This account may be UNTRUSTFUL!==============
Found a potential vulnerability at line 195, column 37 in src/processor.rs
The account info is not trustful:

    193|     let clock_sysvar_account = next_account_info(accounts_iter)?;
    194|     let vesting_account = next_account_info(accounts_iter)?;
   >195|     let vesting_token_account = next_account_info(accounts_iter)?;
    196|     let destination_token_account = next_account_info(accounts_iter)?;
    197|
>>>Stack Trace:
>>>token_vesting::processor::Processor::process_instruction::h4e00bba612d4c7e8  [src/entrypoint.rs:16]
>>>  token_vesting::processor::Processor::process::h4e00bba612d4c7e8
>>>  token_vesting::processor::Processor::process_unlock::h41ef90fc25b6e656 [src/processor.rs:338]
==============This arithmetic operation may be UNSAFE!==============
Found a potential vulnerability. The add operation may result in overflows:

    230|     for s in schedules.iter_mut() {
    231|         if clock.unix_timestamp as u64 >= s.release_time {
   >232|             total_amount_to_transfer += s.amount;
    233|             s.amount = 0;
    234|         }
>>>Stack Trace:
>>>entrypoint
>>>  token_vesting::entrypoint::process_instruction::h6b24fd7947Bfd31b [/root/.cargo/registry/src/github.com-lecc
>>>  token_vesting::processor::Processor::process_instruction::hadaffca4312031bd [src/entrypoint.rs:16]
>>>  token_vesting::processor::Processor::process_unlock::h4efc6856a3178d39 [src/processor.rs:338]

How to install X-Ray

Option 1 (Linux terminal)

sh -c "$(curl -k https://supercompiler.xyz/install)"

\export PATH=$PWD/soteria-linux-develop/bin/:$PATH

Option 2 (Docker)

docker run -v $PWD/jet-v1/:/workspace -it greencorelab/soteria:latest /bin/bash

Jump start

soteria --version

Questions?

Please email contact@sec3.dev

For all blogs by sec3, Please visit https://www.sec3.dev/blog

Related Posts

Tools

Announcing sec3 WatchTower

sec3 announces the first release of WatchTower: an in-situ threat monitoring service for Solana smart contracts to detect, prevent and stop security attacks in real time.

Read more
Tools

Announcing sec3 X-ray Security Scanner

sec3 X-ray scanner software is a security scanner specifically designed for Solana smart contracts. sec3 X-ray can detect more than 50 types of security vulnerabilities and can be integrated into the GitHub CI development process. Integrating sec3 X-ray into your protocol's development process can shift security practices left, reduce costly security issues, and speed up time-to-market. sec3 Xray has been adopted at leading Solana Protocols; try it out today!

Read more
Tools

CashioApp Attack - What’s the Vulnerability and How Soteria Detects It

The Cashio stablecoin (CASH) protocol recently lost $50M in an attack. The attacker was able to mint 2,000,000,000 CASH tokens for almost free. The root cause is a vulnerability in the Cashio’s brrr smart contract. Soteria team conducted an in-depth analysis of the attack. Importantly, the vulnerability can be automatically detected by Soteria’s Premium Auto Auditor. This article elaborates on the details.

Read more