solana-vulnerability-scanner

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Solana Vulnerability Scanner

Solana漏洞扫描器

1. Purpose

1. 用途

Systematically scan Solana programs (native and Anchor framework) for platform-specific security vulnerabilities related to cross-program invocations, account validation, and program-derived addresses. This skill encodes 6 critical vulnerability patterns unique to Solana's account model.
系统性扫描Solana程序(原生程序和Anchor框架)中与跨程序调用、账户验证和程序派生地址相关的平台特定安全漏洞。该技能包含了6种Solana账户模型特有的关键漏洞模式。

2. When to Use This Skill

2. 适用场景

  • Auditing Solana programs (native Rust or Anchor)
  • Reviewing cross-program invocation (CPI) logic
  • Validating program-derived address (PDA) implementations
  • Pre-launch security assessment of Solana protocols
  • Reviewing account validation patterns
  • Assessing instruction introspection logic
  • 审计Solana程序(原生Rust或Anchor框架)
  • 审查跨程序调用(CPI)逻辑
  • 验证程序派生地址(PDA)实现
  • Solana协议上线前的安全评估
  • 审查账户验证模式
  • 评估指令内省逻辑

3. Platform Detection

3. 平台检测

File Extensions & Indicators

文件扩展名与识别标识

  • Rust files:
    .rs
  • Rust文件
    .rs

Language/Framework Markers

语言/框架标记

rust
// Native Solana program indicators
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    pubkey::Pubkey,
    program::invoke,
    program::invoke_signed,
};

entrypoint!(process_instruction);

// Anchor framework indicators
use anchor_lang::prelude::*;

#[program]
pub mod my_program {
    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        // Program logic
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
}

// Common patterns
AccountInfo, Pubkey
invoke(), invoke_signed()
Signer<'info>, Account<'info>
#[account(...)] with constraints
seeds, bump
rust
// Native Solana program indicators
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    pubkey::Pubkey,
    program::invoke,
    program::invoke_signed,
};

entrypoint!(process_instruction);

// Anchor framework indicators
use anchor_lang::prelude::*;

#[program]
pub mod my_program {
    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        // Program logic
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
}

// Common patterns
AccountInfo, Pubkey
invoke(), invoke_signed()
Signer<'info>, Account<'info>
#[account(...)] with constraints
seeds, bump

Project Structure

项目结构

  • programs/*/src/lib.rs
    - Program implementation
  • Anchor.toml
    - Anchor configuration
  • Cargo.toml
    with
    solana-program
    or
    anchor-lang
  • tests/
    - Program tests
  • programs/*/src/lib.rs
    - 程序实现文件
  • Anchor.toml
    - Anchor配置文件
  • 包含
    solana-program
    anchor-lang
    依赖的
    Cargo.toml
  • tests/
    - 程序测试目录

Tool Support

工具支持

  • Trail of Bits Solana Lints: Rust linters for Solana
  • Installation: Add to Cargo.toml
  • anchor test: Built-in testing framework
  • Solana Test Validator: Local testing environment

  • Trail of Bits Solana Lints:针对Solana的Rust代码检查工具
  • 安装方式:添加至Cargo.toml
  • anchor test:内置测试框架
  • Solana Test Validator:本地测试环境

4. How This Skill Works

4. 工作原理

When invoked, I will:
  1. Search your codebase for Solana/Anchor programs
  2. Analyze each program for the 6 vulnerability patterns
  3. Report findings with file references and severity
  4. Provide fixes for each identified issue
  5. Check account validation and CPI security

调用该技能后,我会:
  1. 搜索代码库中的Solana/Anchor程序
  2. 分析每个程序是否存在6种漏洞模式
  3. 报告检测结果,包含文件引用和风险等级
  4. 为每个问题提供修复方案
  5. 检查账户验证和CPI安全性

5. Example Output

5. 示例输出



5. Vulnerability Patterns (6 Patterns)

5. 漏洞模式(6种)

I check for 6 critical vulnerability patterns unique to Solana. For detailed detection patterns, code examples, mitigations, and testing strategies, see VULNERABILITY_PATTERNS.md.
我会检查Solana特有的6类关键漏洞模式。如需详细的检测模式、代码示例、缓解措施和测试策略,请查看VULNERABILITY_PATTERNS.md

Pattern Summary:

模式摘要:

  1. Arbitrary CPI ⚠️ CRITICAL - User-controlled program IDs in CPI calls
  2. Improper PDA Validation ⚠️ CRITICAL - Using create_program_address without canonical bump
  3. Missing Ownership Check ⚠️ HIGH - Deserializing accounts without owner validation
  4. Missing Signer Check ⚠️ CRITICAL - Authority operations without is_signer check
  5. Sysvar Account Check ⚠️ HIGH - Spoofed sysvar accounts (pre-Solana 1.8.1)
  6. Improper Instruction Introspection ⚠️ MEDIUM - Absolute indexes allowing reuse
For complete vulnerability patterns with code examples, see VULNERABILITY_PATTERNS.md.
  1. 任意CPI ⚠️ 严重 - CPI调用中使用用户可控的程序ID
  2. 不当PDA验证 ⚠️ 严重 - 使用create_program_address但未使用规范的bump值
  3. 缺失所有权检查 ⚠️ 高风险 - 反序列化账户时未验证所有者
  4. 缺失签名者检查 ⚠️ 严重 - 权限操作未进行is_signer检查
  5. 系统变量账户检查 ⚠️ 高风险 - 伪造的系统变量账户(Solana 1.8.1版本之前)
  6. 不当指令内省 ⚠️ 中风险 - 使用绝对索引导致指令可被重复利用
如需包含代码示例的完整漏洞模式,请查看VULNERABILITY_PATTERNS.md

5. Scanning Workflow

5. 扫描流程

Step 1: Platform Identification

步骤1:平台识别

  1. Verify Solana program (native or Anchor)
  2. Check Solana version (1.8.1+ for sysvar security)
  3. Locate program source (
    programs/*/src/lib.rs
    )
  4. Identify framework (native vs Anchor)
  1. 验证是否为Solana程序(原生或Anchor框架)
  2. 检查Solana版本(1.8.1+版本修复了系统变量安全问题)
  3. 定位程序源码(
    programs/*/src/lib.rs
  4. 识别使用的框架(原生 vs Anchor)

Step 2: CPI Security Review

步骤2:CPI安全性审查

bash
undefined
bash
undefined

Find all CPI calls

查找所有CPI调用

rg "invoke(|invoke_signed(" programs/
rg "invoke(|invoke_signed(" programs/

Check for program ID validation before each

检查每个调用前是否有程序ID验证

Should see program ID checks immediately before invoke

应在invoke调用前看到程序ID检查逻辑


For each CPI:
- [ ] Program ID validated before invocation
- [ ] Cannot pass user-controlled program accounts
- [ ] Anchor: Uses `Program<'info, T>` type

针对每个CPI调用:
- [ ] 调用前验证程序ID
- [ ] 不允许传递用户可控的程序账户
- [ ] Anchor框架:使用`Program<'info, T>`类型

Step 3: PDA Validation Check

步骤3:PDA验证检查

bash
undefined
bash
undefined

Find PDA usage

查找PDA使用情况

rg "find_program_address|create_program_address" programs/ rg "seeds.*bump" programs/
rg "find_program_address|create_program_address" programs/ rg "seeds.*bump" programs/

Anchor: Check for seeds constraints

Anchor框架:检查seeds约束

rg "#[account.*seeds" programs/

For each PDA:
- [ ] Uses `find_program_address()` or Anchor `seeds` constraint
- [ ] Bump seed stored and reused
- [ ] Not using user-provided bump
rg "#[account.*seeds" programs/

针对每个PDA:
- [ ] 使用`find_program_address()`或Anchor的`seeds`约束
- [ ] 存储并复用bump种子
- [ ] 不使用用户提供的bump值

Step 4: Account Validation Sweep

步骤4:账户验证全面检查

bash
undefined
bash
undefined

Find account deserialization

查找账户反序列化操作

rg "try_from_slice|try_deserialize" programs/
rg "try_from_slice|try_deserialize" programs/

Should see owner checks before deserialization

应在反序列化前看到所有者检查逻辑

rg ".owner\s*==|.owner\s*!=" programs/

For each account used:
- [ ] Owner validated before deserialization
- [ ] Signer check for authority accounts
- [ ] Anchor: Uses `Account<'info, T>` and `Signer<'info>`
rg ".owner\s*==|.owner\s*!=" programs/

针对每个使用的账户:
- [ ] 反序列化前验证所有者
- [ ] 权限账户进行签名者检查
- [ ] Anchor框架:使用`Account<'info, T>`和`Signer<'info>`类型

Step 5: Instruction Introspection Review

步骤5:指令内省审查

bash
undefined
bash
undefined

Find instruction introspection usage

查找指令内省使用情况

rg "load_instruction_at|load_current_index|get_instruction_relative" programs/
rg "load_instruction_at|load_current_index|get_instruction_relative" programs/

Check for checked versions

检查是否使用带检查的版本

rg "load_instruction_at_checked|load_current_index_checked" programs/

- [ ] Using checked functions (Solana 1.8.1+)
- [ ] Using relative indexing
- [ ] Proper correlation validation
rg "load_instruction_at_checked|load_current_index_checked" programs/

- [ ] 使用带检查的函数(Solana 1.8.1+)
- [ ] 使用相对索引
- [ ] 进行正确的关联验证

Step 6: Trail of Bits Solana Lints

步骤6:Trail of Bits Solana代码检查

toml
undefined
toml
undefined

Add to Cargo.toml

添加至Cargo.toml

[dependencies] solana-program = "1.17" # Use latest version
[lints.clippy]
[dependencies] solana-program = "1.17" # 使用最新版本
[lints.clippy]

Enable Solana-specific lints

启用Solana特定的代码检查规则

(Trail of Bits solana-lints if available)

(若可用,启用Trail of Bits的solana-lints)


---

---

6. Reporting Format

6. 报告格式

Finding Template

检测结果模板

markdown
undefined
markdown
undefined

[CRITICAL] Arbitrary CPI - Unchecked Program ID

[严重] 任意CPI - 未检查程序ID

Location:
programs/vault/src/lib.rs:145-160
(withdraw function)
Description: The
withdraw
function performs a CPI to transfer SPL tokens without validating that the provided
token_program
account is actually the SPL Token program. An attacker can provide a malicious program that appears to perform a transfer but actually steals tokens or performs unauthorized actions.
Vulnerable Code:
rust
// lib.rs, line 145
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let token_program = &ctx.accounts.token_program;

    // WRONG: No validation of token_program.key()!
    invoke(
        &spl_token::instruction::transfer(...),
        &[
            ctx.accounts.vault.to_account_info(),
            ctx.accounts.destination.to_account_info(),
            ctx.accounts.authority.to_account_info(),
            token_program.to_account_info(),  // UNVALIDATED
        ],
    )?;
    Ok(())
}
Attack Scenario:
  1. Attacker deploys malicious "token program" that logs transfer instruction but doesn't execute it
  2. Attacker calls withdraw() providing malicious program as token_program
  3. Vault's authority signs the transaction
  4. Malicious program receives CPI with vault's signature
  5. Malicious program can now impersonate vault and drain real tokens
Recommendation: Use Anchor's
Program<'info, Token>
type:
rust
use anchor_spl::token::{Token, Transfer};

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(mut)]
    pub vault: Account<'info, TokenAccount>,
    #[account(mut)]
    pub destination: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,  // Validates program ID automatically
}

pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let cpi_accounts = Transfer {
        from: ctx.accounts.vault.to_account_info(),
        to: ctx.accounts.destination.to_account_info(),
        authority: ctx.accounts.authority.to_account_info(),
    };

    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        cpi_accounts,
    );

    anchor_spl::token::transfer(cpi_ctx, amount)?;
    Ok(())
}
References:
  • building-secure-contracts/not-so-smart-contracts/solana/arbitrary_cpi
  • Trail of Bits lint:
    unchecked-cpi-program-id

---
位置
programs/vault/src/lib.rs:145-160
(withdraw函数)
描述:
withdraw
函数执行CPI调用以转移SPL代币,但未验证传入的
token_program
账户是否为真实的SPL Token程序。攻击者可提供恶意程序,伪装成执行转账操作,实则窃取代币或执行未授权操作。
漏洞代码:
rust
// lib.rs, line 145
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let token_program = &ctx.accounts.token_program;

    // 错误:未验证token_program.key()!
    invoke(
        &spl_token::instruction::transfer(...),
        &[
            ctx.accounts.vault.to_account_info(),
            ctx.accounts.destination.to_account_info(),
            ctx.accounts.authority.to_account_info(),
            token_program.to_account_info(),  // 未验证
        ],
    )?;
    Ok(())
}
攻击场景:
  1. 攻击者部署恶意“代币程序”,该程序仅记录转账指令但不实际执行
  2. 攻击者调用withdraw(),将恶意程序作为token_program传入
  3. 金库的权限账户签署交易
  4. 恶意程序收到带有金库签名的CPI调用
  5. 恶意程序可伪装成金库并盗取真实代币
修复建议: 使用Anchor的
Program<'info, Token>
类型:
rust
use anchor_spl::token::{Token, Transfer};

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(mut)]
    pub vault: Account<'info, TokenAccount>,
    #[account(mut)]
    pub destination: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,  // 自动验证程序ID
}

pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let cpi_accounts = Transfer {
        from: ctx.accounts.vault.to_account_info(),
        to: ctx.accounts.destination.to_account_info(),
        authority: ctx.accounts.authority.to_account_info(),
    };

    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        cpi_accounts,
    );

    anchor_spl::token::transfer(cpi_ctx, amount)?;
    Ok(())
}
参考资料:
  • building-secure-contracts/not-so-smart-contracts/solana/arbitrary_cpi
  • Trail of Bits代码检查规则:
    unchecked-cpi-program-id

---

7. Priority Guidelines

7. 优先级指南

Critical (Immediate Fix Required)

严重(需立即修复)

  • Arbitrary CPI (attacker-controlled program execution)
  • Improper PDA validation (account spoofing)
  • Missing signer check (unauthorized access)
  • 任意CPI(攻击者可控的程序执行)
  • 不当PDA验证(账户伪造)
  • 缺失签名者检查(未授权访问)

High (Fix Before Launch)

高风险(上线前修复)

  • Missing ownership check (fake account data)
  • Sysvar account check (authentication bypass, pre-1.8.1)
  • 缺失所有权检查(伪造账户数据)
  • 系统变量账户检查(身份验证绕过,1.8.1版本之前)

Medium (Address in Audit)

中风险(审计时处理)

  • Improper instruction introspection (logic bypass)

  • 不当指令内省(逻辑绕过)

8. Testing Recommendations

8. 测试建议

Unit Tests

单元测试

rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn test_rejects_wrong_program_id() {
        // Provide wrong program ID, should fail
    }

    #[test]
    #[should_panic]
    fn test_rejects_non_canonical_pda() {
        // Provide non-canonical bump, should fail
    }

    #[test]
    #[should_panic]
    fn test_requires_signer() {
        // Call without signature, should fail
    }
}
rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn test_rejects_wrong_program_id() {
        // 传入错误的程序ID,应执行失败
    }

    #[test]
    #[should_panic]
    fn test_rejects_non_canonical_pda() {
        // 传入非规范的bump值,应执行失败
    }

    #[test]
    #[should_panic]
    fn test_requires_signer() {
        // 无签名调用,应执行失败
    }
}

Integration Tests (Anchor)

集成测试(Anchor框架)

typescript
import * as anchor from "@coral-xyz/anchor";

describe("security tests", () => {
  it("rejects arbitrary CPI", async () => {
    const fakeTokenProgram = anchor.web3.Keypair.generate();

    try {
      await program.methods
        .withdraw(amount)
        .accounts({
          tokenProgram: fakeTokenProgram.publicKey, // Wrong program
        })
        .rpc();

      assert.fail("Should have rejected fake program");
    } catch (err) {
      // Expected to fail
    }
  });
});
typescript
import * as anchor from "@coral-xyz/anchor";

describe("security tests", () => {
  it("rejects arbitrary CPI", async () => {
    const fakeTokenProgram = anchor.web3.Keypair.generate();

    try {
      await program.methods
        .withdraw(amount)
        .accounts({
          tokenProgram: fakeTokenProgram.publicKey, // 错误的程序
        })
        .rpc();

      assert.fail("Should have rejected fake program");
    } catch (err) {
      // 预期执行失败
    }
  });
});

Solana Test Validator

Solana测试验证器

bash
undefined
bash
undefined

Run local validator for testing

启动本地测试验证器

solana-test-validator
solana-test-validator

Deploy and test program

部署并测试程序

anchor test

---
anchor test

---

9. Additional Resources

9. 额外资源



10. Quick Reference Checklist

10. 快速参考检查清单

Before completing Solana program audit:
CPI Security (CRITICAL):
  • ALL CPI calls validate program ID before
    invoke()
  • Cannot use user-provided program accounts
  • Anchor: Uses
    Program<'info, T>
    type
PDA Security (CRITICAL):
  • PDAs use
    find_program_address()
    or Anchor
    seeds
    constraint
  • Bump seed stored and reused (not user-provided)
  • PDA accounts validated against canonical address
Account Validation (HIGH):
  • ALL accounts check owner before deserialization
  • Native: Validates
    account.owner == expected_program_id
  • Anchor: Uses
    Account<'info, T>
    type
Signer Validation (CRITICAL):
  • ALL authority accounts check
    is_signer
  • Native: Validates
    account.is_signer == true
  • Anchor: Uses
    Signer<'info>
    type
Sysvar Security (HIGH):
  • Using Solana 1.8.1+
  • Using checked functions:
    load_instruction_at_checked()
  • Sysvar addresses validated
Instruction Introspection (MEDIUM):
  • Using relative indexes for correlation
  • Proper validation between related instructions
  • Cannot reuse same instruction across multiple calls
Testing:
  • Unit tests cover all account validation
  • Integration tests with malicious inputs
  • Local validator testing completed
  • Trail of Bits lints enabled and passing
完成Solana程序审计前:
CPI安全性(严重):
  • 所有CPI调用在
    invoke()
    前验证程序ID
  • 不允许使用用户提供的程序账户
  • Anchor框架:使用
    Program<'info, T>
    类型
PDA安全性(严重):
  • PDA使用
    find_program_address()
    或Anchor的
    seeds
    约束
  • 存储并复用bump种子(不使用用户提供的值)
  • PDA账户与规范地址进行验证
账户验证(高风险):
  • 所有账户在反序列化前检查所有者
  • 原生程序:验证
    account.owner == expected_program_id
  • Anchor框架:使用
    Account<'info, T>
    类型
签名者验证(严重):
  • 所有权限账户检查
    is_signer
  • 原生程序:验证
    account.is_signer == true
  • Anchor框架:使用
    Signer<'info>
    类型
系统变量安全性(高风险):
  • 使用Solana 1.8.1+版本
  • 使用带检查的函数:
    load_instruction_at_checked()
  • 验证系统变量地址
指令内省(中风险):
  • 使用相对索引进行关联
  • 对相关指令进行正确验证
  • 不允许在多个调用中复用同一指令
测试:
  • 单元测试覆盖所有账户验证逻辑
  • 使用恶意输入进行集成测试
  • 完成本地验证器测试
  • 启用并通过Trail of Bits代码检查