solana-vulnerability-scanner
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSolana 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, bumprust
// 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, bumpProject Structure
项目结构
- - Program implementation
programs/*/src/lib.rs - - Anchor configuration
Anchor.toml - with
Cargo.tomlorsolana-programanchor-lang - - Program tests
tests/
- - 程序实现文件
programs/*/src/lib.rs - - Anchor配置文件
Anchor.toml - 包含或
solana-program依赖的anchor-langCargo.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:
- Search your codebase for Solana/Anchor programs
- Analyze each program for the 6 vulnerability patterns
- Report findings with file references and severity
- Provide fixes for each identified issue
- Check account validation and CPI security
调用该技能后,我会:
- 搜索代码库中的Solana/Anchor程序
- 分析每个程序是否存在6种漏洞模式
- 报告检测结果,包含文件引用和风险等级
- 为每个问题提供修复方案
- 检查账户验证和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:
模式摘要:
- Arbitrary CPI ⚠️ CRITICAL - User-controlled program IDs in CPI calls
- Improper PDA Validation ⚠️ CRITICAL - Using create_program_address without canonical bump
- Missing Ownership Check ⚠️ HIGH - Deserializing accounts without owner validation
- Missing Signer Check ⚠️ CRITICAL - Authority operations without is_signer check
- Sysvar Account Check ⚠️ HIGH - Spoofed sysvar accounts (pre-Solana 1.8.1)
- Improper Instruction Introspection ⚠️ MEDIUM - Absolute indexes allowing reuse
For complete vulnerability patterns with code examples, see VULNERABILITY_PATTERNS.md.
- 任意CPI ⚠️ 严重 - CPI调用中使用用户可控的程序ID
- 不当PDA验证 ⚠️ 严重 - 使用create_program_address但未使用规范的bump值
- 缺失所有权检查 ⚠️ 高风险 - 反序列化账户时未验证所有者
- 缺失签名者检查 ⚠️ 严重 - 权限操作未进行is_signer检查
- 系统变量账户检查 ⚠️ 高风险 - 伪造的系统变量账户(Solana 1.8.1版本之前)
- 不当指令内省 ⚠️ 中风险 - 使用绝对索引导致指令可被重复利用
如需包含代码示例的完整漏洞模式,请查看VULNERABILITY_PATTERNS.md。
5. Scanning Workflow
5. 扫描流程
Step 1: Platform Identification
步骤1:平台识别
- Verify Solana program (native or Anchor)
- Check Solana version (1.8.1+ for sysvar security)
- Locate program source ()
programs/*/src/lib.rs - Identify framework (native vs Anchor)
- 验证是否为Solana程序(原生或Anchor框架)
- 检查Solana版本(1.8.1+版本修复了系统变量安全问题)
- 定位程序源码()
programs/*/src/lib.rs - 识别使用的框架(原生 vs Anchor)
Step 2: CPI Security Review
步骤2:CPI安全性审查
bash
undefinedbash
undefinedFind 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
undefinedbash
undefinedFind 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 bumprg "#[account.*seeds" programs/
针对每个PDA:
- [ ] 使用`find_program_address()`或Anchor的`seeds`约束
- [ ] 存储并复用bump种子
- [ ] 不使用用户提供的bump值Step 4: Account Validation Sweep
步骤4:账户验证全面检查
bash
undefinedbash
undefinedFind 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
undefinedbash
undefinedFind 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 validationrg "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
undefinedtoml
undefinedAdd 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
undefinedmarkdown
undefined[CRITICAL] Arbitrary CPI - Unchecked Program ID
[严重] 任意CPI - 未检查程序ID
Location: (withdraw function)
programs/vault/src/lib.rs:145-160Description:
The function performs a CPI to transfer SPL tokens without validating that the provided 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.
withdrawtoken_programVulnerable 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:
- Attacker deploys malicious "token program" that logs transfer instruction but doesn't execute it
- Attacker calls withdraw() providing malicious program as token_program
- Vault's authority signs the transaction
- Malicious program receives CPI with vault's signature
- Malicious program can now impersonate vault and drain real tokens
Recommendation:
Use Anchor's type:
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>, // 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
---位置:(withdraw函数)
programs/vault/src/lib.rs:145-160描述:
函数执行CPI调用以转移SPL代币,但未验证传入的账户是否为真实的SPL Token程序。攻击者可提供恶意程序,伪装成执行转账操作,实则窃取代币或执行未授权操作。
withdrawtoken_program漏洞代码:
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(())
}攻击场景:
- 攻击者部署恶意“代币程序”,该程序仅记录转账指令但不实际执行
- 攻击者调用withdraw(),将恶意程序作为token_program传入
- 金库的权限账户签署交易
- 恶意程序收到带有金库签名的CPI调用
- 恶意程序可伪装成金库并盗取真实代币
修复建议:
使用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
undefinedbash
undefinedRun local validator for testing
启动本地测试验证器
solana-test-validator
solana-test-validator
Deploy and test program
部署并测试程序
anchor test
---anchor test
---9. Additional Resources
9. 额外资源
- Building Secure Contracts:
building-secure-contracts/not-so-smart-contracts/solana/ - Trail of Bits Solana Lints: https://github.com/trailofbits/solana-lints
- Anchor Documentation: https://www.anchor-lang.com/
- Solana Program Library: https://github.com/solana-labs/solana-program-library
- Solana Cookbook: https://solanacookbook.com/
- Building Secure Contracts:
building-secure-contracts/not-so-smart-contracts/solana/ - Trail of Bits Solana Lints:https://github.com/trailofbits/solana-lints
- Anchor文档:https://www.anchor-lang.com/
- Solana程序库:https://github.com/solana-labs/solana-program-library
- Solana开发指南:https://solanacookbook.com/
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 type
Program<'info, T>
PDA Security (CRITICAL):
- PDAs use or Anchor
find_program_address()constraintseeds - 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 type
Account<'info, T>
Signer Validation (CRITICAL):
- ALL authority accounts check
is_signer - Native: Validates
account.is_signer == true - Anchor: Uses type
Signer<'info>
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调用在前验证程序ID
invoke() - 不允许使用用户提供的程序账户
- Anchor框架:使用类型
Program<'info, T>
PDA安全性(严重):
- PDA使用或Anchor的
find_program_address()约束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代码检查