mason4fu/smart-contract-vuln-scanner
GitHub: mason4fu/smart-contract-vuln-scanner
针对 Solidity/EVM 智能合约的静态漏洞扫描器,通过源码 AST 和字节码双重分析来检测重入、访问控制、未检查外部调用及算术溢出等常见安全漏洞。
Stars: 0 | Forks: 0
# 智能合约漏洞扫描器
一个用于检测 Solidity 智能合约漏洞的静态分析工具。
支持**源码级 (AST)** 和**字节码级 (EVM)** 分析。
## 状态
课程项目已**完成**:所有计划的检测器、端到端扫描 pipeline、评估脚本以及最终报告工件均已就绪。
| 领域 | 状态 |
|------|--------|
| 课程交付物 | ✅ 完成 |
| 仓库结构 | ✅ 完成 |
| Python 包脚手架 | ✅ 完成 |
| Foundry 工作区 | ✅ 完成 |
| CI 流水线 | ✅ 完成 |
| 冒烟测试 | ✅ 完成 |
| 漏洞检测器 | ✅ 完成 (access-control, reentrancy, unchecked external calls, arithmetic) |
| 完整分析 pipeline | ✅ 完成 |
| 数据集与评估 | ✅ 完成 (见 [评估](#evaluation-datasets)) |
该扫描器提供了 access-control、reentrancy、unchecked external call 和 arithmetic 检测器,并支持源码级 AST 分析和字节码级 EVM 分析。运行 `uv run scanner scan ` 即可扫描合约。
## 用法
### 扫描 Solidity 文件
```
uv run scanner scan contracts/MyContract.sol
uv run scanner scan contracts/MyContract.sol --format json
uv run scanner scan contracts/MyContract.sol --format sarif
```
### 扫描目录
```
uv run scanner scan contracts/ --format text
```
### 扫描预编译输出
```
uv run scanner scan out/MyContract.json
```
### 扫描原始字节码
```
uv run scanner scan MyContract.bin
```
### 选项
| 选项 | 默认值 | 描述 |
|--------|---------|-------------|
| `--format`, `-f` | `text` | 输出格式:`json`、`text` 或 `sarif` |
| `--output`, `-o` | `reports/` | 报告输出目录 |
| `--detector`, `-d` | all | 仅运行特定的检测器 |
| `--bytecode-only` | false | 跳过源码分析 |
| `--solc-version` | `0.8.28` | Solidity 编译器版本 |
## 检测器
### 访问控制
检测 Solidity 合约中的访问控制漏洞。
| 规则 | ID | 严重程度 | 描述 |
|------|----|----------|-------------|
| tx.origin 认证 | SWC-115 | HIGH | 使用 tx.origin 而不是 msg.sender 进行授权 |
| 可调用的类似构造函数的初始化 | SWC-118 | HIGH/MEDIUM | 外部可调用的、会改变所有权的类似构造函数的初始化 |
| 缺少权限校验 | SWC-105 | HIGH | 没有授权检查的敏感 public/external 函数 |
| 未初始化的 Owner | SWC-105 | MEDIUM | 声明了 Owner 变量但从未在构造函数中赋值 |
| 危险的放弃所有权 | SWC-106 | LOW | 没有两步转移保护的 renounceOwnership() |
| 无防护的角色授予 | SWC-105 | HIGH | 没有授权守卫的角色/特权授予函数 |
详见 [`docs/access-control-detector.md`](docs/access-control-detector.md)。
### 重入
检测 Solidity 合约中潜在的重入漏洞(在状态生效之前的外部交互 / checks-effects-interactions 风险)。
| 规则 | ID | 严重程度 | 描述 |
|------|----|----------|-------------|
| 状态更新前的外部调用 | SWC-107 | HIGH | 函数在随后的状态写入之前执行外部 CALL 系列交互 (违反 checks-effects-interactions 的风险) |
| 字节码印证 (置信度提升) | SWC-107 | MEDIUM/HIGH 置信度 | 已部署的运行时字节码在稍后的 SSTORE 之前显示了 CALL 系列操作码,用作印证性启发式方法 |
示例:
```
uv run scanner scan tests/fixtures/ReentrancyPatterns.sol --detector reentrancy
uv run scanner scan contracts/src --detector reentrancy --format json
```
详见 `src/scanner/detectors/reentrancy.py` 和 `tests/test_reentrancy_detector.py`。
### 未检查的 external-calls
检测 Solidity 底层外部调用中未检查成功处理的问题。
| 规则 | ID | 严重程度 | 描述 |
|------|----|----------|-------------|
| 未检查的底层调用结果 | SWC-104 | MEDIUM | `.call`、`.delegatecall`、`.staticcall` 或 `.send` 的成功结果被忽略、丢弃或未作为失败关卡使用 |
| 模糊的字节码调用处理 | SWC-104 | LOW/MEDIUM | 运行时字节码包含成功处理不明确的 CALL 系列操作码,在没有源码的情况下无法确定 |
示例:
```
uv run scanner scan tests/fixtures/UncheckedExternalCalls.sol --detector unchecked-external-calls
uv run scanner scan contracts/src --detector unchecked-external-calls --format json
uv run scanner scan out/MyContract.sol/MyContract.json --detector unchecked-external-calls
uv run scanner scan sample.bin --detector unchecked-external-calls --bytecode-only --format json
```
该检测器将 `require(success)`、`assert(success)`、`if (!success) revert`,以及有界的成功别名(如 `handled = success` 或 `failed = !success`)、private/internal 辅助检查、将成功结果返回给调用者,以及仅通过事件观察失败且后续没有延续操作的情况均视为已处理。
它不会针对高级别类型化的外部调用或 `.transfer(...)` 进行检测。
详见 [`docs/unchecked-external-calls.md`](docs/unchecked-external-calls.md)。
### 算术
检测风险算术操作中潜在的整数溢出/下溢 (SWC-101):
`+`、`-`、`*`、复合赋值 (`+=`、`-=`、`*=`) 以及一元运算符 `++` / `--`。
**版本控制:** Solidity `>=0.8.0` 默认使用检查算术,因此检测器只报告 `unchecked { ... }` 内的算术运算,除非 pragma 解析为 0.8 之前的版本。
**抑制规则 (精度优先):** 跳过 `library` 合约和构造函数;识别 SafeMath 风格的 `.add` / `.sub` / `.mul` 用法;将经典的加法溢出守卫 (如 `require(a + b >= a)`) 视为在操作数匹配时对 `+` 的充分保护。
| 规则 | ID | 严重程度 | 描述 |
|------|----|----------|-------------|
| 未检查的状态/记账算术 | SWC-101 | HIGH | 风险算术在没有可识别守卫的情况下更新了状态/记账值 |
| 敏感路径算术构造 | SWC-101 | HIGH/MEDIUM | 算术运算参与了 transfer/mint/burn 或其他对值敏感的行为 |
| 字节码算术提示 | SWC-101 | LOW | 纯字节码中 `SSTORE` 附近的 `ADD`/`SUB`/`MUL` 启发式检测 (低置信度) |
示例:
```
uv run scanner scan tests/fixtures/ArithmeticPatterns.sol --detector arithmetic --solc-version 0.4.25
uv run scanner scan tests/fixtures/ArithmeticSafe08.sol --detector arithmetic --format json
uv run scanner scan tests/fixtures/ArithmeticUnchecked08.sol --detector arithmetic --format json
```
```
uv run scanner scan tests/fixtures/ArithmeticPatterns.sol --detector arithmetic --solc-version 0.4.25
uv run scanner scan sample.bin --detector arithmetic --bytecode-only --format json
```
行级基准标签位于 [`datasets/arithmetic/`](datasets/arithmetic/) 下。
针对该基准真值运行 [`scripts/evaluate_arithmetic.py`](scripts/evaluate_arithmetic.py)。
完整的规则/抑制矩阵见 [`docs/arithmetic-detector-spec.md`](docs/arithmetic-detector-spec.md)。
实现:`src/scanner/detectors/arithmetic.py` · 测试:`tests/test_arithmetic_detector.py`
## 评估数据集
- **访问控制**
- SmartBugs Curated — 编译 `15/18`,精确率 `1.000`,召回率 `1.000`,F1 `1.000`
- Not-So-Smart-Contracts — 编译 `3/3`,精确率 `1.000`,召回率 `1.000`,F1 `1.000`
- SWC Registry 固定子集 — 编译 `10/10`,精确率 `1.000`,召回率 `1.000`,F1 `1.000`
- **未检查的外部调用 (Unchecked external calls)**:
- SmartBugs 未检查子集 — 精确率 1.000,召回率 1.000,F1=1.000
- SolidiFI Unhandled-Exceptions 范围子集 — 精确率 1.000,召回率 0.898,F1=0.946
- Not-So-Smart-Contracts 未检查外部调用 — 精确率 1.000,召回率 1.000,F1=1.000
- 主要范围汇总 — 精确率 1.000,召回率 0.918,F1=0.957
- 原始全标签诊断汇总 — 精确率 1.000,召回率 0.508,F1=0.673
- 留出的 Slither unchecked-lowlevel/unchecked-send 固定数据集 — 精确率 1.000,召回率 1.000,F1=1.000
- **算术 (SmartBugs 策划算术子集,行级 ±6)**:
- 编译 15/15
- TP=21, FP=1, FN=2
- 精确率 0.955,召回率 0.913,F1=0.933
- **重入 (SmartBugs 策划重入子集,结构化启发式)**:
- 编译 31/31
- 合约召回率 31/31 = 1.000
- ±3 行内的行重叠率:30/31 = 0.968
用于最终报告的已保存基准工件位于 `reports/final-report/` 下。
参见 `reports/final-report/summary.md` 和 [`docs/final-report-prep.md`](docs/final-report-prep.md)。
为了 CI 风格的集成,扫描器还可以输出 SARIF:
```
uv run scanner scan contracts/ --format sarif --output reports
```
这会生成一个适用于代码扫描工作流的 `.sarif` 文件,以及一个用于多文件扫描的 `*.project-summary.json` 工件。
最终报告的基线比较包括:
- `Slither 0.11.5` 在重叠的 access-control、unchecked-call 和 reentrancy 切片上的表现
- 一个简单的 SWC-104 语法基线,用于与语义 `stored but not used` 检测器进行对比
来自 `reports/final-report/summary.md` 的主要比较点:
- 访问控制 / SmartBugs:我们的扫描器 `P/R/F1 = 1.000/1.000/1.000`,Slither `0.700/0.368/0.483`
- 未检查的外部调用 / SolidiFI 范围子集:我们的扫描器召回率 `0.898`,Slither 召回率 `0.695`
- 重入 / SmartBugs:我们的扫描器编译通过 `31/31`,行召回率为 `0.968`;Slither 在其编译通过子集上实现了 `1.000` 的行召回率,但仅编译通过了 `29/31`
复现评估(访问控制、未检查调用、算术、基线):
```
uv run python scripts/evaluate_smartbugs.py
uv run python scripts/evaluate_smartbugs.py --output results.json
uv run python scripts/evaluate_nssc.py
uv run python scripts/fetch_swc_registry.py
uv run python scripts/evaluate_swc_registry.py
uv run python scripts/fetch_unchecked_call_datasets.py
uv run python scripts/evaluate_unchecked_calls.py
uv run python scripts/evaluate_arithmetic.py --output reports/arithmetic-eval.json
uv run python scripts/evaluate_baselines.py --output reports/final-report/baselines.json
uv run python scripts/collect_final_report_metrics.py
```
方法和结果详见 [`docs/evaluation.md`](docs/evaluation.md)。
## 仓库结构
```
smart-contract-vuln-scanner/
├── src/scanner/ # Python scanner package
│ ├── cli.py # CLI entrypoint (Typer)
│ ├── config.py # Configuration (Pydantic)
│ ├── compiler/ # Solidity compilation via py-solc-x
│ ├── ast/ # AST extraction and traversal
│ ├── bytecode/ # Bytecode loading and disassembly
│ ├── models/ # Shared data models (Finding, Severity)
│ ├── output/ # Report rendering (JSON, text)
│ └── utils/ # Path resolution, helpers
├── contracts/
│ ├── src/ # Solidity fixture contracts
│ └── test/ # Foundry tests
├── tests/ # Python tests (pytest)
├── scripts/ # PowerShell scripts (Windows-friendly)
│ ├── setup.ps1 # Bootstrap environment
│ ├── test.ps1 # Run all tests
│ └── verify.ps1 # Full CI-equivalent check
├── samples/
│ ├── contracts/ # Sample Solidity inputs
│ └── compiled/ # Compiled output artifacts
├── reports/ # Generated scan reports
├── docs/ # Developer documentation
├── lib/ # Foundry dependencies (forge-std)
├── .github/workflows/ # GitHub Actions CI
├── pyproject.toml # Python project config
├── foundry.toml # Foundry config
└── Makefile # Make targets (optional)
```
## 前置条件
| 工具 | 版本 | 安装 |
|------|---------|---------|
| **Python** | ≥ 3.12 | [python.org](https://www.python.org/downloads/) |
| **uv** | 最新版 | `irm https://astral.sh/uv/install.ps1 \| iex` |
| **Foundry** | 最新版 | 见下方 [Foundry 安装](#foundry-setup) |
| **Git** | 任意版本 | [git-scm.com](https://git-scm.com/) |
## Windows 快速入门
```
# 1. Clone repo
git clone https://github.com/mason4fu/smart-contract-vuln-scanner.git
cd smart-contract-vuln-scanner
# 2. Run setup script(安装 deps、pre-commit hooks)
pwsh scripts/setup.ps1
# 3. Run 所有 tests
pwsh scripts/test.ps1
# 4. 全面验证(lint + format + tests + forge)
pwsh scripts/verify.ps1
```
### Python 环境设置
```
# 安装 uv(如果尚未安装)
irm https://astral.sh/uv/install.ps1 | iex
# 同步依赖
uv sync
# Run Python tests
uv run pytest
# Run CLI
uv run scanner --help
uv run scanner --version
```
### Foundry 设置
下载 Windows 版 Foundry:
```
# 下载最新 release
$url = "https://github.com/foundry-rs/foundry/releases/latest/download/foundry_nightly_win32_amd64.zip"
Invoke-WebRequest -Uri $url -OutFile foundry.zip
Expand-Archive foundry.zip -DestinationPath "$env:USERPROFILE\.foundry\bin" -Force
# 添加到 PATH(将此添加到你的 PowerShell profile 以实现持久化)
$env:Path = "$env:USERPROFILE\.foundry\bin;$env:Path"
# 验证
forge --version
```
然后构建并测试合约:
```
forge build
forge test -v
```
## 运行检查
### 仅 Python
```
uv run pytest # tests
uv run ruff check src/ tests/ # lint
uv run ruff format src/ tests/ # auto-format
```
### 仅 Foundry
```
forge build # compile contracts
forge test -v # run Solidity tests
```
### 完整验证
```
pwsh scripts/verify.ps1 # everything at once
```
## 分支命名规范
| 前缀 | 用途 |
|--------|---------|
| `feature/` | 新功能或检测器 |
| `fix/` | Bug 修复 |
| `setup/` | 工具 / 基础设施 |
| `docs/` | 文档变更 |
| `test/` | 测试新增 |
示例:`feature/reentrancy-detector`、`fix/ast-loader-crash`、`docs/add-examples`
## 贡献流程
如果您 fork 了本仓库或在课程提交后添加后续工作,请使用此流程。
1. 从 `master` 创建分支:`git checkout -b feature/my-detector`
2. 以小而集中的提交进行更改
3. 在推送之前运行 `pwsh scripts/verify.ps1`
4. 推送并开启 Pull Request
5. 在合并前至少获得一位审查者的批准
## 我们交付了什么
- **检测器:** `access-control`、`reentrancy`、`unchecked-external-calls`、`arithmetic` (规则和文档链接在上方的 [检测器](#detectors) 中)。
- **输出:** JSON、纯文本和 SARIF;通过 `--output` 输出多文件项目摘要- **证据:** [评估](#evaluation-datasets) 中的数据集数据;已保存的工件位于 `reports/final-report/` 下 (参见 `reports/final-report/summary.md`)。
## 扩展扫描器 (可选)
新的检测器应遵循与现有检测器相同的模式:
1. 位于 `src/scanner/detectors/` 下的模块中 (例如 `reentrancy.py`)。
2. 接受编译器输出 (AST 和/或字节码) 作为输入。
3. 发出 `Finding` 对象 (`scanner.models.findings`)。
4. 在有用时,于 `tests/` 中包含测试并在 `contracts/src/` 中包含 fixtures。
**不在本次提交范围内的想法:** 时间戳依赖、弱随机性、抢跑启发式方法或更深入的路径敏感分析。
**架构指引**
- `scanner.compiler.solc` – 编译 Solidity 并获取 AST + bytecode
- `scanner.ast.loader` – 提取并遍历 AST
- `scanner.bytecode.loader` – 从编译器输出中提取 bytecode
- `scanner.bytecode.disasm` – 通过 pyevmasm 反汇编 bytecode
- `scanner.models.findings` – 所有检测器生成的 `Finding` 模型
- `scanner.output.report` – 将 findings 渲染到报告中
完整的设计概述见 [`docs/architecture.md`](docs/architecture.md)。
## 许可证
[MIT](LICENSE)
标签:EVM, Python, SARIF, Solidity, Streamlit, Web3安全, 云安全监控, 以太坊, 区块链安全, 图数据库, 无后门, 智能合约, 未检查外部调用, 算术溢出, 访问控制, 逆向工具, 重入攻击, 静态分析