关于 blockchain-auditor
面向安全审计人员的智能合约审计 Skill,可检查已验证源码或未验证字节码,并按漏洞发现、分叉验证和风险评级组织审查流程。
主要功能
- 01源码与字节码侦察
- 02检查常见合约漏洞
- 03分析 DeFi 特定攻击面
- 04基于分叉验证漏洞利用
- 05漏洞评级与报告生成
适用场景
- 01审查已验证的智能合约
- 02调查仅有字节码的合约
- 03在分叉环境测试 DeFi 漏洞
- 04编写漏洞报告
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ ckorhonen/ claude-skills --skill blockchain-auditor安装后在 Claude Code 中输入/blockchain-auditor确认已加载。
附带文件reference.md
详细介绍
区块链安全审计员
系统地审计智能合约,找出可被利用的漏洞,使无特权账户能够提取资金或获得未经授权的访问权限。
快速开始
# For verified contracts (Etherscan source available)
cast etherscan-source <address> --chain mainnet
# For unverified contracts (bytecode only)
cast code <address> --rpc-url $ETH_RPC_URL
cast disassemble <bytecode>
# Fork testing
forge test --fork-url $ETH_RPC_URL -vvv
# Static analysis
slither . --checklist
审计方法
阶段 1:侦察
-
收集合约信息:
# Check balance cast balance <address> --rpc-url $ETH_RPC_URL # Get bytecode cast code <address> --rpc-url $ETH_RPC_URL # Check if verified on Etherscan curl "https://api.etherscan.io/api?module=contract&action=getsourcecode&address=<address>&apikey=$ETHERSCAN_API_KEY" # Check for proxy implementation cast storage <address> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $ETH_RPC_URL -
识别合约类型:
- 多签钱包(addOwner、execute、confirmTransaction)
- 代币合约(transfer、approve、balanceOf)
- DeFi 协议(deposit、withdraw、swap、liquidate)
- 借贷协议(Compound/Aave 风格)
- AMM/DEX(Uniswap v2/v3 风格,带流动性池)
- 代理模式(EIP-1967、UUPS、Transparent、Beacon)
- 金库/收益聚合器(ERC-4626)
- 跨链桥(lock/mint、burn/release)
- NFT 合约(ERC-721、ERC-1155)
阶段 2:源代码分析(已验证合约)
-
识别高风险函数:
selfdestruct/suicide- 可耗尽所有 ETHdelegatecall- 可执行任意代码- 带用户可控数据的
call- 可执行任意调用 - 不带检查的
transfer/send- 重入风险 withdraw/claim/redeem- 资金提取点initialize/init- 代理初始化(能否被调用两次?)permit/permitAll- 绕过链下授权flash/flashLoan- 闪电贷入口点
-
检查访问控制模式:
// Weak patterns to look for: require(tx.origin == owner); // tx.origin bypass require(msg.sender == owner); // Check if owner is compromised // Missing modifier on sensitive function function withdraw() public { // No onlyOwner! payable(msg.sender).transfer(address(this).balance); } // Proxy: unprotected initialize function initialize(address _owner) public { // Missing initializer modifier! owner = _owner; } -
已知漏洞模式:
- 重入(在外部调用后才更改状态)
- 整数溢出/下溢(Solidity < 0.8.0)
- 未检查返回值
- 易受抢先交易影响
- 闪电贷攻击(在单笔交易中操纵价格)
- 价格预言机操纵(现货价格与 TWAP)
- Permit/EIP-2612 签名重放
- ERC777 回调重入
- 只读重入(在逻辑中误用 view 函数)
- 捐赠攻击(代币余额膨胀)
- 首位存款者份额膨胀(ERC-4626 金库)
阶段 3:DeFi 专属攻击向量
对于 DeFi 协议,以下攻击向量价值最高且常被利用:
-
价格预言机操纵:
// Vulnerable: spot price used as oracle function getPrice() public view returns (uint256) { (uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(pool).getReserves(); return reserve1 * 1e18 / reserve0; // Manipulable in a single tx! } // Attack: flash loan → manipulate pool → call vulnerable function → repay- 检查协议是否在任何地方使用现货价格
- TWAP 预言机是安全的;Chainlink 数据源是安全的(在心跳间隔内)
- 查找
getReserves()、slot0()、直接查询 AMM 池的调用
-
闪电贷攻击面:
- 是否可以使用借入的资金调用任何状态更改函数?
- 检查:借入大额资金 → 执行操作 → 在同一笔交易中偿还
- 关键问题:协议是在用户存款前还是存款后测量价格/余额?
-
重入变体:
// Cross-function reentrancy function withdraw() external { // State updated after transfer → reenter via deposit() IERC20(token).safeTransfer(msg.sender, amounts[msg.sender]); amounts[msg.sender] = 0; // Too late! } // Read-only reentrancy (in Curve, Balancer) // Attack: during callback, totalSupply() returns old value // Another protocol reads it via getVirtualPrice() during the callback -
ERC-4626 金库膨胀攻击:
- 首位存款者可通过捐赠代币抬高份额价格
- 目标:以
(deposit * totalShares) / totalAssets铸造份额的协议 - 如果
totalShares = 0,首位存款者将获得deposit份额;随后可通过捐赠抬高份额价格
-
签名/Permit 漏洞:
// EIP-2612 permit can be front-run // Front-runner takes the permit, uses it themselves before victim's tx // Safe if permit is used atomically in the same tx // Check: does permit use chainId? (cross-chain replay) // Check: does it use nonces? (replay protection) -
治理/时间锁攻击:
- 治理机制能否提议并执行恶意升级?
- 是否设有时间锁?时长是多少?
- 能否通过闪电贷获取投票权以绕过法定人数?
阶段 4:字节码分析(未经验证的合约)
-
反汇编字节码:
cast disassemble <bytecode> > contract.asm -
提取函数选择器:
# Look for PUSH4 followed by 4 bytes grep -oE '63[0-9a-f]{8}' contract.asm | cut -c3-10 | sort -u -
查询签名:
curl "https://www.4byte.directory/api/v1/signatures/?hex_signature=0x<selector>" # Also try openchain.xyz/signature-database -
识别危险操作码:
操作码 十六进制 风险 SELFDESTRUCT ff严重 - 销毁合约 DELEGATECALL f4高 - 执行任意代码 CALL f1中 - 外部调用 CALLCODE f2高 - 已弃用,危险 CREATE2 f5中 - 确定性部署 TLOAD/TSTORE 5c/5dEIP-1153 新增 - 瞬态存储 -
分析控制流:
- 查找函数入口点的 JUMPDEST 位置
- 跟踪 CALLER/ORIGIN 检查以识别访问控制
- 查找用于状态访问的 SLOAD/SSTORE 模式
阶段 5:漏洞评级
根据当前区块链状态评估每项发现被利用的可能性:
| 评级 | 标准 | 操作 |
|---|---|---|
| 严重 | 目前可直接利用,价值高 | 立即编写 PoC |
| 高 | 满足特定条件后可利用 | 进行分叉测试 |
| 中 | 需要不太可能出现的条件 | 记录 |
| 低 | 仅存在理论可能,相关条件极不可能出现 | 仅作备注 |
影响可能性的因素:
- 当前区块链状态(所有者密钥、时间锁、余额)
- 所需前置条件(存款、授权、区块编号)
- Gas 成本与潜在收益的比较
- MEV/抢先交易风险(攻击者能否进行三明治攻击?)
- Flashbots 私有内存池的可用性
阶段 6:分叉验证
-
设置 Foundry 测试:
// test/Exploit.t.sol pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "forge-std/interfaces/IERC20.sol"; interface IFlashLoanProvider { function flashLoan(uint256 amount) external; } contract ExploitTest is Test { address target = 0x<TARGET_ADDRESS>; address attacker; function setUp() public { // Fork at specific block for determinism vm.createSelectFork(vm.envString("ETH_RPC_URL"), <BLOCK_NUMBER>); attacker = makeAddr("attacker"); vm.deal(attacker, 1 ether); } function test_exploit() public { uint256 balanceBefore = attacker.balance; vm.prank(attacker); (bool success,) = target.call(abi.encodeWithSelector(0x<SELECTOR>)); uint256 balanceAfter = attacker.balance; // CRITICAL: Verify actual fund extraction assertGt(balanceAfter, balanceBefore, "Exploit failed - no funds extracted"); } // Flash loan exploit template function test_flashLoanExploit() public { uint256 targetBalanceBefore = IERC20(token).balanceOf(target); vm.prank(attacker); FlashLoanAttacker exploitContract = new FlashLoanAttacker(target); exploitContract.attack(); assertLt(IERC20(token).balanceOf(target), targetBalanceBefore, "No drain"); assertGt(IERC20(token).balanceOf(attacker), 0, "No profit"); } } -
在分叉上运行:
forge test --fork-url $ETH_RPC_URL -vvvv --match-test "test_exploit" -
验证资金是否被提取(而不只是调用成功):
- 检查攻击者余额是否增加
- 验证目标余额是否减少
- 确认合约状态是否按预期改变
- 调用成功并不意味着利用成功
阶段 7:生成报告
对于已确认的漏洞,创建报告:
# Vulnerability Report: [Contract Address]
## Summary
- **Severity**: Critical/High/Medium/Low
- **Type**: [Reentrancy/Access Control/Oracle Manipulation/etc.]
- **Impact**: [Amount at risk, what attacker gains]
- **Exploitable**: Yes/No (with current blockchain state)
- **Block confirmed**: [Block number of fork test]
## Vulnerable Function
\`\`\`solidity
function vulnerableFunction() public {
// Vulnerable code
}
\`\`\`
## Attack Vector
1. Attacker calls function X with parameter Y
2. Contract fails to check Z
3. Funds transferred to attacker
## Proof of Concept
\`\`\`solidity
// Foundry test that demonstrates the exploit
function test_exploit() public {
// Setup and exploit code
}
\`\`\`
## Execution Script (if confirmed)
\`\`\`bash
cast send <target> "vulnerableFunction()" --rpc-url $ETH_RPC_URL --private-key $PRIVATE_KEY
\`\`\`
## Remediation
- Add access control modifier
- Implement checks-effects-interactions pattern
- Use SafeMath for arithmetic (or upgrade to Solidity >= 0.8.0)
- Replace spot price with TWAP oracle
常见漏洞检查清单
访问控制
- 所有敏感函数都使用了适当的修饰符
- 所有者/管理员地址未被攻破
- 多签需要足够数量的确认
- 时间锁得到执行
- 未使用 tx.origin 进行身份验证
- 代理初始化函数无法被重复调用
- UUPS 升级函数受到访问控制
- 治理法定人数不能通过闪电贷绕过
重入
- 外部调用前先更改状态(CEI 模式)
- 资金转账使用了 ReentrancyGuard
- 不会回调不可信合约
- 已考虑 ERC-777 tokensReceived 钩子
- 已检查跨函数重入
- 已检查只读重入(getVirtualPrice、汇率)
算术(Solidity < 0.8.0)
- 所有运算都使用 SafeMath
- 不存在处理用户输入的 unchecked 代码块
- 边界检查正确
- 检查 0.5.x - 0.6.x 溢出漏洞
预言机 / 价格操纵
- 不使用现货价格预言机(可被闪电贷操纵)
- 在适用时使用 TWAP
- 检查 Chainlink 心跳时效性
- 关键路径使用多预言机聚合
- 不将池余额用作价格来源
闪电贷
- 状态不能被操纵后又在同一笔交易中读取
- 不会在不可信外部调用后进行价格/比率检查
- 所有可通过闪电贷调用的函数都设有重入保护
DeFi 特定检查
- ERC-4626 已防护首个存款人份额通胀攻击(虚拟份额/资产)
- Permit/EIP-2612 有重放保护(nonce、chainId)
- 针对基于余额的记账提供了捐赠攻击防护
- 在适用时提供 MEV 三明治攻击防护
- 跨链桥:提供消息重放保护
外部调用
- 检查返回值
- 适当设置 Gas 限制
- 正确处理 Fallback/receive 函数
Solidity 版本特定问题
| 版本 | 问题 | 检查项 |
|---|---|---|
| < 0.8.0 | 整数溢出/下溢 | SafeMath 的使用 |
| < 0.6.0 | 构造函数名称混淆 | constructor() 关键字 |
| < 0.5.0 | 未初始化的存储指针 | 变量声明 |
| Any | tx.origin 身份验证 | 访问控制模式 |
| Any | 代理存储冲突 | 是否使用 EIP-1967 槽位 |
误报指标
注意以下看似可利用、实则不可利用的模式:
- 多签待处理交易:
kill()可以成功,但需要 N-of-M 确认 - 用户专属余额:
withdraw()只会返还调用者存入的金额 - 时间锁定的释放:资金会转入硬编码地址,而非调用者地址
- 代理模式:即使代理没有检查,实现合约也可能有检查
- 调用成功但没有转账:函数执行完成,但没有 ETH 转移
- 附带即时还款检查的闪电贷:如果在结束时验证余额,就无法利用
- 可暂停合约:管理员可以暂停合约,但这是设计如此
在判定漏洞可利用之前,务必先在分叉环境中验证资金是否实际发生转移。
工具参考
| 工具 | 用途 | 命令 |
|---|---|---|
| cast | RPC 调用、反汇编 | cast code/call/send/disassemble |
| forge | 分叉测试 | forge test --fork-url |
| anvil | 本地分叉节点 | anvil --fork-url $RPC |
| slither | 静态分析 | slither . --checklist |
| 4byte.directory | 选择器查询 | API 或网页 |
| openchain.xyz | 选择器查询 | API 或网页 |
| Etherscan | 源代码、ABI | API 或网页 |
| Mythril | 符号执行 | myth analyze |
| Tenderly | 交易模拟 | 网页 UI |
| Dedaub | 字节码反编译器 | dedaub.com |
环境设置
所需环境变量:
export ETH_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/<KEY>"
export ETHERSCAN_API_KEY="<KEY>"
所需工具:
# Foundry (forge, cast, anvil)
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Slither
pip3 install slither-analyzer
# Node.js (for Hardhat if needed)
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
示例工作流
# 1. Check contract value and code
cast balance 0x<address> --rpc-url $ETH_RPC_URL
cast code 0x<address> --rpc-url $ETH_RPC_URL > bytecode.hex
# 2. Check for proxy (EIP-1967 slot)
cast storage 0x<address> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $ETH_RPC_URL
# 3. Disassemble if unverified
cast disassemble $(cat bytecode.hex) > contract.asm
# 4. Extract and lookup selectors
grep -oE '63[0-9a-f]{8}' contract.asm | cut -c3-10 | sort -u | while read sel; do
sig=$(curl -s "https://www.4byte.directory/api/v1/signatures/?hex_signature=0x$sel" | jq -r '.results[0].text_signature // "unknown"')
echo "0x$sel: $sig"
done
# 5. Run slither on source (if available)
slither . --checklist 2>&1 | head -100
# 6. Create and run fork test
forge test --fork-url $ETH_RPC_URL -vvvv
# 7. Verify fund extraction (not just call success!)
# 8. Generate report if confirmed exploitable
数据库集成
审计多个合约时,请在 SQLite 中跟踪发现:
CREATE TABLE contracts (
address TEXT PRIMARY KEY,
balance_usd REAL,
is_verified INTEGER,
exploitable INTEGER DEFAULT 0,
attack_type TEXT,
notes TEXT
);
-- Update after analysis
UPDATE contracts SET
exploitable = 0,
notes = 'Multi-sig wallet. kill() requires confirmations. NOT exploitable.'
WHERE address = '0x...';
安全与法律说明
- 仅在分叉环境中测试;未经明确授权,切勿在主网上测试
- 记录所有发现,包括误报
- 对于线上漏洞,请考虑负责任地披露
- 注意执行漏洞利用可能涉及的法律问题
- 此技能仅用于经授权的安全研究
- Immunefi、Code4rena、Sherlock — 请通过官方漏洞赏金渠道报告
评论
支持 Markdown