name: substrate-vulnerability-scanner description: 扫描Substrate/Polkadot pallets以检测7种关键漏洞,包括算术溢出、panic DoS、不正确的权重和错误来源检查。用于审计Substrate runtimes或FRAME pallets。
Substrate漏洞扫描器
1. 目的
系统扫描Substrate runtime模块(pallets)以检测平台特定的安全漏洞,这些漏洞可能导致节点崩溃、DoS攻击或未授权访问。此技能编码了7种Substrate/FRAME基础链独有的关键漏洞模式。
2. 何时使用此技能
- 审计自定义Substrate pallets
- 审查FRAME runtime代码
- Substrate链(Polkadot parachains、独立链)预发布安全评估
- 验证可调度外部函数
- 审查权重计算函数
- 评估无签名交易验证逻辑
3. 平台检测
文件扩展名和指标
- Rust文件:
.rs
语言/框架标记
// Substrate/FRAME指标
#[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 { }
}
}
// 常见模式
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- Runtime配置benchmarking.rs- 权重基准测试Cargo.toml包含frame-*依赖
工具支持
- cargo-fuzz: Rust模糊测试
- test-fuzz: 基于属性的测试框架
- 基准测试框架: 内置权重计算
- try-runtime: Runtime迁移测试
4. 此技能如何工作
当调用时,我将:
- 搜索您的代码库以查找Substrate pallets
- 分析每个pallet以检测7种漏洞模式
- 报告发现包括文件引用和严重性
- 提供修复针对每个识别出的问题
- 检查权重计算和来源验证
5. 漏洞模式(7种关键模式)
我检查7种Substrate/FRAME独有的关键漏洞模式。有关详细检测模式、代码示例、缓解措施和测试策略,请参见VULNERABILITY_PATTERNS.md。
模式摘要:
-
算术溢出 ⚠️ 关键
- 直接
+、-、*、/操作符在发布模式下包装 - 必须使用
checked_*或saturating_*方法 - 影响余额/代币计算、奖励/费用数学
- 直接
-
不Panic ⚠️ 关键 - DoS
- Panic导致节点停止处理区块
- 无
unwrap()、expect()、无边界检查的数组索引 - 所有用户输入必须用
ensure!验证
-
权重和费用 ⚠️ 关键 - DoS
- 不正确的权重允许垃圾邮件攻击
- 可变成本操作的固定权重导致DoS
- 必须使用基准测试框架,绑定所有输入参数
-
先验证,后写入 ⚠️ 高(v0.9.25之前)
- 验证前的存储写入在错误时持久化(v0.9.25之前)
- 模式:验证 → 写入 → 发出事件
- 升级到v0.9.25+或使用手动
#[transactional]
-
无签名交易验证 ⚠️ 高
- 不足的验证允许垃圾邮件/重放攻击
- 优先使用签名交易
- 如果无签名:验证参数、重放保护、验证来源
-
坏随机性 ⚠️ 中
pallet_randomness_collective_flip易受合谋攻击- 必须使用BABE随机性(
pallet_babe::RandomnessFromOneEpochAgo) - 使用
random(subject)而不是random_seed()
-
坏来源 ⚠️ 关键
ensure_signed允许任何用户进行特权操作- 必须使用
ensure_root或自定义来源(ForceOrigin、AdminOrigin) - 来源类型必须在runtime中正确配置
有关完整的漏洞模式代码示例,请参见VULNERABILITY_PATTERNS.md。
6. 扫描工作流
步骤1:平台识别
- 验证Substrate/FRAME框架使用
- 检查Substrate版本(v0.9.25+具有事务性存储)
- 定位pallet实现(
pallets/*/lib.rs) - 识别runtime配置(
runtime/lib.rs)
步骤2:可调度分析
对于每个#[pallet::call]函数:
- [ ] 算术:使用检查/饱和操作?
- [ ] Panic:无unwrap/expect/索引?
- [ ] 权重:与成本成比例,输入有界?
- [ ] 来源:适当的验证级别?
- [ ] 验证:所有检查在存储写入前?
步骤3:Panic扫描
# 搜索易panic的模式
rg "unwrap\(\)" pallets/
rg "expect\(" pallets/
rg "\[.*\]" pallets/ # 数组索引
rg " as u\d+" pallets/ # 类型转换
rg "\.unwrap_or" pallets/
步骤4:算术安全检查
# 查找直接算术
rg " \+ |\+=| - |-=| \* |\*=| / |/=" pallets/
# 应该找到检查/饱和替代方案
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:来源和权限审查
# 查找特权操作
rg "ensure_signed" pallets/ | grep -E "pause|emergency|admin|force|sudo"
# 应该使用ensure_root或自定义来源
rg "ensure_root|ForceOrigin|AdminOrigin" pallets/
步骤7:测试审查
- [ ] 单元测试覆盖所有可调度
- [ ] 模糊测试用于panic条件
- [ ] 基准测试用于权重计算
- [ ] try-runtime测试用于迁移
7. 优先级指南
关键(需立即修复)
- 算术溢出(代币创建、余额操作)
- Panic DoS(节点崩溃风险)
- 坏来源(未授权特权操作)
高(发布前修复)
- 不正确的权重(通过垃圾邮件的DoS)
- 先验证违规(状态损坏,v0.9.25之前)
- 无签名验证问题(垃圾邮件、重放攻击)
中(审计中处理)
- 坏随机性(操纵可能但影响有限)
8. 测试推荐
模糊测试
// 使用test-fuzz进行基于属性的测试
#[cfg(test)]
mod tests {
use test_fuzz::test_fuzz;
#[test_fuzz]
fn fuzz_transfer(from: AccountId, to: AccountId, amount: u128) {
// 应该永不panic
let _ = Pallet::transfer(from, to, amount);
}
#[test_fuzz]
fn fuzz_no_panics(call: Call) {
// 无可调度应该panic
let _ = call.dispatch(origin);
}
}
基准测试
# 运行基准测试以生成权重
cargo build --release --features runtime-benchmarks
./target/release/node benchmark pallet \
--chain dev \
--pallet pallet_example \
--extrinsic "*" \
--steps 50 \
--repeat 20
try-runtime
# 测试runtime升级
cargo build --release --features try-runtime
try-runtime --runtime ./target/release/wbuild/runtime.wasm \
on-runtime-upgrade live --uri wss://rpc.polkadot.io
9. 附加资源
- 构建安全合约:
building-secure-contracts/not-so-smart-contracts/substrate/ - Substrate文档:https://docs.substrate.io/
- FRAME文档:https://paritytech.github.io/substrate/master/frame_support/
- test-fuzz:https://github.com/trailofbits/test-fuzz
- Substrate StackExchange:https://substrate.stackexchange.com/
10. 快速参考清单
完成Substrate pallet审计前:
算术安全(关键):
- [ ] 无可调度中直接的
+、-、*、/操作符 - [ ] 所有算术使用
checked_*或saturating_* - [ ] 类型转换使用
try_into()和错误处理
Panic预防(关键):
- [ ] 无可调度中的
unwrap()或expect() - [ ] 无边界检查的直接数组/切片索引
- [ ] 所有用户输入用
ensure!验证 - [ ] 除法操作检查零除数
权重和DoS(关键):
- [ ] 权重与计算成本成比例
- [ ] 输入参数有最大界限
- [ ] 使用基准测试确定权重
- [ ] 无免费(零权重)昂贵操作
访问控制(关键):
- [ ] 特权操作使用
ensure_root或自定义来源 - [ ]
ensure_signed仅用于用户级操作 - [ ] 来源类型在runtime中正确配置
- [ ] Sudo pallet在生产前移除
存储安全(高):
- [ ] 使用Substrate v0.9.25+或手动
#[transactional] - [ ] 验证在存储写入前
- [ ] 事件在成功操作后发出
其他(中):
- [ ] 无签名交易如果可能使用签名替代
- [ ] 如果无签名:适当验证、重放保护、身份验证
- [ ] 使用BABE随机性(不是RandomnessCollectiveFlip)
- [ ] 随机性使用
random(subject)而不是random_seed()
测试:
- [ ] 所有可调度的单元测试
- [ ] 模糊测试以查找panic
- [ ] 基准测试生成和验证
- [ ] try-runtime测试用于迁移