名称:cairo-vulnerability-scanner 描述:扫描Cairo/StarkNet智能合约中的6个关键漏洞,包括felt252算术溢出、L1-L2消息传递问题、地址转换问题和签名重放。用于审计StarkNet项目。
Cairo/StarkNet漏洞扫描器
1. 目的
系统性地扫描StarkNet上的Cairo智能合约,查找与算术、跨层消息传递和加密操作相关的平台特定安全漏洞。此技能编码了Cairo/StarkNet生态系统中6个独特的漏洞模式。
2. 何时使用此技能
- 审计StarkNet智能合约(Cairo)
- 审查L1-L2桥接实现
- StarkNet应用程序的预发布安全评估
- 验证跨层消息处理
- 审查签名验证逻辑
- 评估L1处理函数
3. 平台检测
文件扩展名和指示器
- Cairo文件:
.cairo
语言/框架标记
// Cairo合约指示器
#[contract]
mod MyContract {
use starknet::ContractAddress;
#[storage]
struct Storage {
balance: LegacyMap<ContractAddress, felt252>,
}
#[external(v0)]
fn transfer(ref self: ContractState, to: ContractAddress, amount: felt252) {
// 合约逻辑
}
#[l1_handler]
fn handle_deposit(ref self: ContractState, from_address: felt252, amount: u256) {
// L1消息处理器
}
}
// 常见模式
felt252, u128, u256
ContractAddress, EthAddress
#[external(v0)], #[l1_handler], #[constructor]
get_caller_address(), get_contract_address()
send_message_to_l1_syscall
项目结构
src/contract.cairo- 主合约实现src/lib.cairo- 库模块tests/- 合约测试Scarb.toml- Cairo项目配置
工具支持
- Caracal:Trail of Bits的Cairo静态分析器
- 安装:
pip install caracal - 使用:
caracal detect src/ - cairo-test:内置测试框架
- Starknet Foundry:测试和开发工具包
4. 这个技能如何工作
调用时,我将:
- 搜索您的代码库中的Cairo文件
- 分析每个合约以查找6个漏洞模式
- 报告发现,包含文件引用和严重性
- 为每个识别的问题提供修复方案
- 检查L1-L2交互以查找消息传递漏洞
5. 示例输出
当发现漏洞时,您会得到类似这样的报告:
=== CAIRO/STARKNET漏洞扫描结果 ===
---
## 5. 漏洞模式(6个模式)
我检查Cairo/Starknet中6个独特的漏洞模式。有关详细检测模式、代码示例、缓解措施和测试策略,请参阅[VULNERABILITY_PATTERNS.md](resources/VULNERABILITY_PATTERNS.md)。
### 模式摘要:
1. **未检查的算术** ⚠️ 关键 - felt252中的整数溢出/下溢
2. **存储冲突** ⚠️ 关键 - 冲突的存储变量哈希
3. **缺少访问控制** ⚠️ 关键 - 敏感函数上没有调用者验证
4. **不正确的Felt252边界** ⚠️ 高 - 未验证felt252范围
5. **未验证的合约地址** ⚠️ 高 - 使用不受信任的合约地址
6. **缺少调用者验证** ⚠️ 关键 - 没有get_caller_address()检查
有关完整的漏洞模式及代码示例,请参阅[VULNERABILITY_PATTERNS.md](resources/VULNERABILITY_PATTERNS.md)。
## 5. 扫描工作流程
### 步骤1:平台识别
1. 验证Cairo语言和StarkNet框架
2. 检查Cairo版本(Cairo 1.0+ 与旧版Cairo 0)
3. 定位合约文件(`src/*.cairo`)
4. 识别L1-L2桥接合约(如适用)
### 步骤2:算术安全扫描
```bash
# 在算术中查找felt252使用
rg "felt252" src/ | rg "[-+*/]"
# 查找使用felt252的余额/金额存储
rg "felt252" src/ | rg "balance|amount|total|supply"
# 应优先使用u128、u256
步骤3:L1处理器分析
对于每个#[l1_handler]函数:
- [ ] 验证
from_address参数 - [ ] 检查地址不等于零
- [ ] 有适当的访问控制
- [ ] 发出事件以进行监控
步骤4:签名验证审查
对于基于签名的函数:
- [ ] 包括nonce跟踪
- [ ] 使用后增加nonce
- [ ] 域分隔符包括链ID和合约地址
- [ ] 无法重放签名
步骤5:L1-L2桥接审计
如果合约包括桥接功能:
- [ ] L1验证地址 < STARKNET_FIELD_PRIME
- [ ] L1实现消息取消
- [ ] L2在处理器中验证from_address
- [ ] L1 ↔ L2的对称访问控制
- [ ] 测试完整往返流程
步骤6:使用Caracal进行静态分析
# 运行Caracal检测器
caracal detect src/
# 特定检测器
caracal detect src/ --detectors unchecked-felt252-arithmetic
caracal detect src/ --detectors unchecked-l1-handler-from
caracal detect src/ --detectors missing-nonce-validation
6. 报告格式
发现模板
## [关键] L1处理器中未检查的from_address
**位置**:`src/bridge.cairo:145-155`(handle_deposit函数)
**描述**:
`handle_deposit` L1处理器函数未验证`from_address`参数。任何L1合约都可以向此函数发送消息,并为任意用户铸造代币,绕过预期的L1桥接访问控制。
**漏洞代码**:
```rust
// bridge.cairo, line 145
#[l1_handler]
fn handle_deposit(
ref self: ContractState,
from_address: felt252, // 未验证!
user: ContractAddress,
amount: u256
) {
let current_balance = self.balances.read(user);
self.balances.write(user, current_balance + amount);
}
攻击场景:
- 攻击者部署恶意L1合约
- 恶意合约调用
starknetCore.sendMessageToL2(l2Contract, selector, [attacker_address, 1000000]) - L2处理器处理消息而不检查发送者
- 攻击者在未存入任何资金的情况下收到1,000,000代币
- 协议遭受无限铸造漏洞
建议:
验证from_address与授权的L1桥接地址:
#[l1_handler]
fn handle_deposit(
ref self: ContractState,
from_address: felt252,
user: ContractAddress,
amount: u256
) {
// 验证L1发送者
let authorized_l1_bridge = self.l1_bridge_address.read();
assert(from_address == authorized_l1_bridge, '未经授权的L1发送者');
let current_balance = self.balances.read(user);
self.balances.write(user, current_balance + amount);
}
参考:
- building-secure-contracts/not-so-smart-contracts/cairo/unchecked_l1_handler_from
- Caracal检测器:
unchecked-l1-handler-from
---
## 7. 优先级指南
### 关键(立即修复)
- L1处理器中未检查的from_address(无限铸造)
- L1-L2地址转换问题(资金到零地址)
### 高(部署前修复)
- Felt252算术溢出/下溢(余额操纵)
- 缺少签名重放保护(重放攻击)
- L1-L2消息失败而无取消(锁定资金)
### 中(审计中处理)
- 过度受限的L1-L2交互(被困资金)
---
## 8. 测试建议
### 单元测试
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_felt252_overflow() {
// 测试算术边界情况
}
#[test]
#[should_panic]
fn test_unauthorized_l1_handler() {
// 错误的from_address应失败
}
#[test]
fn test_signature_replay_protection() {
// 同一签名两次应失败
}
}
集成测试(与L1)
// 测试完整L1-L2流程
#[test]
fn test_deposit_withdraw_roundtrip() {
// 1. 在L1存款
// 2. 等待L2处理
// 3. 验证L2余额
// 4. 提款到L1
// 5. 验证L1余额恢复
}
Caracal CI集成
# .github/workflows/security.yml
- name: 运行Caracal
run: |
pip install caracal
caracal detect src/ --fail-on high,critical
9. 额外资源
- 构建安全合约:
building-secure-contracts/not-so-smart-contracts/cairo/ - Caracal:https://github.com/crytic/caracal
- Cairo文档:https://book.cairo-lang.org/
- StarkNet文档:https://docs.starknet.io/
- OpenZeppelin Cairo合约:https://github.com/OpenZeppelin/cairo-contracts
10. 快速参考清单
在完成Cairo/StarkNet审计前:
算术安全(高):
- [ ] 没有使用felt252处理余额/金额(使用u128/u256)
- [ ] 或有felt252算术显式边界检查
- [ ] 溢出/下溢场景已测试
L1处理器安全(关键):
- [ ] 所有
#[l1_handler]函数都验证from_address - [ ] from_address与存储的L1合约地址比较
- [ ] 不能通过部署替代L1合约绕过
L1-L2消息传递(高):
- [ ] L1桥接验证地址 < STARKNET_FIELD_PRIME
- [ ] L1桥接实现消息取消
- [ ] L2处理器检查from_address
- [ ] L1 ↔ L2的对称验证规则
- [ ] 完整往返流程已测试
签名安全(高):
- [ ] 签名包括nonce跟踪
- [ ] 每次使用后增加nonce
- [ ] 域分隔符包括链ID和合约地址
- [ ] 签名重放已测试并防止
- [ ] 防止跨链重放
工具使用:
- [ ] Caracal扫描完成,无关键发现
- [ ] 单元测试覆盖所有漏洞场景
- [ ] 集成测试验证L1-L2流程
- [ ] 测试网部署在主线网前测试