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安全, 云安全监控, 以太坊, 区块链安全, 图数据库, 无后门, 智能合约, 未检查外部调用, 算术溢出, 访问控制, 逆向工具, 重入攻击, 静态分析