mcpField
S
substrate-vulnerability-scanner

substrate-vulnerability-scanner

github.com/trailofbits安全7.9k5.2k 次安装今天更新
代码仓库

关于 substrate-vulnerability-scanner

面向 Substrate/FRAME 链审计的安全分析技能,帮助开发者检查自定义 pallet 和运行时代码。它围绕 7 类平台特有漏洞提供扫描流程、证据覆盖表及修复建议。

主要功能

  1. 01检查 7 类 Substrate 漏洞
  2. 02分析 dispatchable 函数与权限
  3. 03审查权重和算术安全
  4. 04输出逐项覆盖表与证据
  5. 05为发现的问题提供修复建议

适用场景

  1. 01审计自定义 Substrate pallet
  2. 02发布前检查链运行时安全
  3. 03复核未签名交易验证逻辑

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills --skill substrate-vulnerability-scanner

安装后在 Claude Code 中输入/substrate-vulnerability-scanner确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
assets/
trail-of-bits-mark.svg
resources/
VULNERABILITY_PATTERNS.md

详细介绍

Substrate 漏洞扫描器

1. 用途

系统地扫描 Substrate 运行时模块(pallet),以发现可能导致节点崩溃、DoS 攻击或未授权访问的平台特定安全漏洞。此技能涵盖 Substrate/FRAME 链独有的 7 种关键漏洞模式。

2. 何时使用此技能

  • 审计自定义 Substrate pallet
  • 审查 FRAME 运行时代码
  • 对 Substrate 链(Polkadot 平行链、独立链)进行上线前安全评估
  • 验证可调度的外部函数
  • 审查权重计算函数
  • 评估无签名交易验证逻辑

3. 平台检测

文件扩展名和标识
  • Rust 文件:.rs
语言/框架标记
// Substrate/FRAME indicators
#[pallet]
pub mod pallet {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    #[pallet::config]
    pub trait Config: frame_system::Config { }

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(10_000)]
        pub fn example_function(origin: OriginFor<T>) -> DispatchResult { }
    }
}

// Common patterns
DispatchResult, DispatchError
ensure!, ensure_signed, ensure_root
StorageValue, StorageMap, StorageDoubleMap
#[pallet::storage]
#[pallet::call]
#[pallet::weight]
#[pallet::validate_unsigned]
项目结构
  • pallets/*/lib.rs - pallet 实现
  • runtime/lib.rs - 运行时配置
  • benchmarking.rs - 权重基准测试
  • 包含 frame-* 依赖项的 Cargo.toml
工具支持
  • cargo-fuzz:Rust 模糊测试
  • test-fuzz:基于属性的测试框架
  • 基准测试框架:内置权重计算
  • try-runtime:运行时迁移测试

4. 此技能的工作方式

调用后,我将:

  1. 搜索你的代码库,查找 Substrate pallet
  2. 分析每个 pallet,检查 7 种漏洞模式
  3. 报告发现的问题,附上文件引用和严重性,并在其上方提供一张包含每种模式判定结果的覆盖率表
  4. 提供修复方案,解决每个已识别的问题
  5. 检查权重计算和来源验证

5. 漏洞模式(7 种关键模式)

我会检查 Substrate/FRAME 独有的 7 种关键漏洞模式。有关详细的检测模式、代码示例、缓解措施和测试策略,请参阅 VULNERABILITY_PATTERNS.md。

模式摘要:
  1. 算术溢出 ⚠️ 严重

    • 直接使用 +、-、*、/ 运算符时,在发布模式下会发生回绕
    • 必须使用 checked_* 或 saturating_* 方法
    • 影响余额/代币计算、奖励/费用计算
  2. 避免 panic ⚠️ 严重 - DoS

    • panic 会导致节点停止处理区块
    • 不得使用 unwrap()、expect() 或未经边界检查的数组索引
    • 必须使用 ensure! 验证所有用户输入
  3. 权重和费用 ⚠️ 严重 - DoS

    • 权重不正确会导致垃圾信息攻击
    • 对成本可变的操作使用固定权重会引发 DoS
    • 必须使用基准测试框架,并对所有输入参数设置边界
  4. 先验证,后写入 ⚠️ 高(v0.9.25 之前)

    • 在验证之前写入存储,发生错误时仍会保留(v0.9.25 之前)
    • 模式:验证 → 写入 → 发出事件
    • 升级到 v0.9.25+,或使用手动 #[transactional]
  5. 无签名交易验证 ⚠️ 高

    • 验证不足会导致垃圾信息/重放攻击
    • 优先使用有签名交易
    • 如果使用无签名交易:验证参数、防止重放、验证来源
  6. 不安全的随机性 ⚠️ 中

    • pallet_randomness_collective_flip 容易受到串通攻击
    • 必须使用 BABE 随机性(pallet_babe::RandomnessFromOneEpochAgo)
    • 使用 random(subject),而不是 random_seed()
  7. 来源权限不当 ⚠️ 严重

    • ensure_signed 允许任意用户执行特权操作
    • 必须使用 ensure_root 或自定义来源(ForceOrigin、AdminOrigin)
    • 必须在运行时中正确配置来源类型

如需查看包含代码示例的完整漏洞模式,请参阅 VULNERABILITY_PATTERNS.md。


6. 扫描工作流

第 1 步:平台识别
  1. 确认使用了 Substrate/FRAME 框架
  2. 检查 Substrate 版本(v0.9.25+ 支持事务性存储)
  3. 定位 pallet 实现(pallets/*/lib.rs)
  4. 识别运行时配置(runtime/lib.rs)
步骤 2:可调度分析

对于每个 #[pallet::call] 函数:

  • 算术:是否使用了检查或饱和运算?
  • Panic:是否没有使用 unwrap/expect/索引操作?
  • 权重:是否与成本成正比,输入是否有界?
  • Origin:验证级别是否恰当?
  • 验证:是否在写入存储前完成所有检查?
步骤 3:Panic 全面检查
# Search for panic-prone patterns
rg "unwrap\(\)" pallets/
rg "expect\(" pallets/
rg "\[.*\]" pallets/  # Array indexing
rg " as u\d+" pallets/  # Type casts
rg "\.unwrap_or" pallets/
步骤 4:算术安全检查
# Find direct arithmetic
rg " \+ |\+=| - |-=| \* |\*=| / |/=" pallets/

# Should find checked/saturating alternatives instead
rg "checked_add|checked_sub|checked_mul|checked_div" pallets/
rg "saturating_add|saturating_sub|saturating_mul" pallets/
步骤 5:权重分析
  • 运行基准测试:cargo test --features runtime-benchmarks
  • 验证权重是否与计算成本相符
  • 检查输入参数是否有界
  • 检查权重计算函数
步骤 6:Origin 与权限审查
# Find privileged operations
rg "ensure_signed" pallets/ | grep -E "pause|emergency|admin|force|sudo"

# Should use ensure_root or custom origins
rg "ensure_root|ForceOrigin|AdminOrigin" pallets/
步骤 7:测试审查
  • 单元测试覆盖所有可调度项
  • 针对 panic 条件的模糊测试
  • 用于权重计算的基准测试
  • 用于迁移的 try-runtime 测试
步骤 8:报告覆盖情况

报告 §5 中的每种模式,无论是否发现问题。此技能没有发现项模板,因此报告以表格开头,随后列出发现项,并保留全部 7 行:

#模式结论证据
1算术溢出foundsrc/lib.rs:212 -- do_transfer 中对 BalanceOf<T> 使用 +
2不要 Panic
3权重与费用
4先验证,后写入
5无符号交易验证
6随机数质量差
7Origin 不当

结论只能是以下之一:

  • found — 引用 file:line,并在表格后完整描述发现项。
  • clear — 此模式适用于该 pallet,且 pallet 已妥善处理。指出你检查过的宏、origin 检查或算术方法,以便读者能够重复检查。
  • n/a — 此模式不适用于此处。用一个短语说明原因(“此 pallet 不接受无符号交易”)。没有检查不等于 n/a。注意,模式 4 与版本有关(v0.9.25 之前):说明 pallet 针对的 runtime 版本,而不是省略该行。

少于 7 行的表格属于扫描不完整,必须如实报告。Verdict 单元格为空的行同样属于不完整:上方第 1 行已填写,用来展示格式;完成报告前,每一行都必须按同样方式填写。7 个 clear 结论也是读者可以据此采取行动的结果。只覆盖 3 种模式、对其余 4 种不作说明的报告,看起来与一个没有问题的 pallet 毫无区别,而这正是该表格旨在避免的失败。


7. 优先级指南

严重(需立即修复)
  • 算术溢出(代币创建、余额操纵)
  • Panic DoS(节点崩溃风险)
  • Origin 不当(未经授权的特权操作)
高(上线前修复)
  • 权重不正确(通过垃圾请求实施 DoS)
  • 违反先验证原则(状态损坏,v0.9.25 之前)
  • 无符号验证问题(垃圾请求、重放攻击)
中(在审计中处理)
  • 随机数质量差(可能遭到操纵,但影响有限)

8. 测试建议

模糊测试
// Use test-fuzz for property-based testing
#[cfg(test)]
mod tests {
    use test_fuzz::test_fuzz;

    #[test_fuzz]
    fn fuzz_transfer(from: AccountId, to: AccountId, amount: u128) {
        // Should never panic
        let _ = Pallet::transfer(from, to, amount);
    }

    #[test_fuzz]
    fn fuzz_no_panics(call: Call) {
        // No dispatchable should panic
        let _ = call.dispatch(origin);
    }
}
基准测试
# Run benchmarks to generate weights
cargo build --release --features runtime-benchmarks
./target/release/node benchmark pallet \
    --chain dev \
    --pallet pallet_example \
    --extrinsic "*" \
    --steps 50 \
    --repeat 20
try-runtime
# Test runtime upgrades
cargo build --release --features try-runtime
try-runtime --runtime ./target/release/wbuild/runtime.wasm \
    on-runtime-upgrade live --uri wss://rpc.polkadot.io

9. 补充资源


10. 快速参考清单

完成 Substrate pallet 审计前:

算术安全(严重):

  • 可调度函数中不使用直接的 +、-、*、/ 运算符
  • 所有算术运算都使用 checked_* 或 saturating_*
  • 类型转换使用 try_into() 并处理错误

防止 panic(严重):

  • 可调度函数中不使用 unwrap() 或 expect()
  • 不进行未经边界检查的数组/切片直接索引
  • 使用 ensure! 验证所有用户输入
  • 除法运算会检查除数是否为零

权重与 DoS(严重):

  • 权重与计算成本成正比
  • 输入参数设有最大值限制
  • 使用基准测试确定权重
  • 不存在免费的(零权重)高成本操作

访问控制(严重):

  • 特权操作使用 ensure_root 或自定义来源
  • ensure_signed 仅用于用户级操作
  • 在运行时正确配置来源类型
  • 在投入生产前移除 Sudo pallet

存储安全(高):

  • 使用 Substrate v0.9.25+,或手动添加 #[transactional]
  • 在写入存储前完成验证
  • 操作成功后再发出事件

其他(中):

  • 如果可行,无签名交易应使用有签名的替代方案
  • 如果使用无签名交易:进行适当的验证、防重放保护和身份验证
  • 使用 BABE 随机性(而非 RandomnessCollectiveFlip)
  • 随机性使用 random(subject),而非 random_seed()

测试:

  • 为所有可调度函数编写单元测试
  • 使用模糊测试查找 panic
  • 生成并验证基准测试
  • 为迁移编写 try-runtime 测试
  • 输出包含全部 7 行的覆盖率表,每行均标注 found、clear 或 n/a,并说明原因

11. 应拒绝的合理化说法

  • “这个 pallet 很小,所以大多数模式显然都不适用。” 对谁来说显然?写一个 n/a 只需一个 从句,就能让判断可供审查。沉默什么也没记录,读者无法分辨这是不适用,还是根本没有检查。
  • “编译时没有警告,所以算术运算没问题。” 发布构建会静默溢出。对 Balance 使用 + 就属于模式 1, 无论编译器是否提示;而且生产节点不会运行调试断言。
  • “我检查了这个 pallet 中重要的模式。” 判断哪些模式重要,本身就是扫描工作,而不是开始扫描的前提。 应在完成表格后再按严重程度排序,而不是省略某些行。
  • “没有发现问题,所以没什么可报告的。” 即使扫描未发现任何问题,也仍要输出完整的覆盖率表。 这张表就是交付内容:它能区分经过审查的 pallet 和只是被粗略看过的 pallet。
  • “ensure_signed 是一种来源检查。” 它只能确认有人签了名,而不能确认签名者是否正确。模式 7 关注的是 所需的来源类型;在本应使用 ensure_root 的地方使用 ensure_signed,看似符合这种说法,实际却没有通过检查。
  • “权重经过基准测试,所以是正确的。” 基准测试只能测量其实际执行的路径。如果可调度函数的最坏情况取决于存储大小, 或取决于无界循环,即使附有基准测试,也仍属于模式 3。

评论

支持 Markdown