Solidity智能合约安全攻防实战:从重入攻击到闪电贷完整审计
·
Solidity智能合约安全攻防实战:从重入攻击到闪电贷完整审计
一、引言
2022年DeFi黑客攻击损失超38亿美元,90%来自合约漏洞。最贵的一课:Wormhole桥3.26亿美元(签名验证绕过)。本文将复现Top5真实漏洞并构建完整审计工具链。
二、重入攻击(Reentrancy)
2.1 经典重入:DAO攻击(损失6000万美元)
// ❌ 有漏洞的合约
contract VulnerableBank {
mapping(address => uint256) public balances;
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// ★ 先转账,后更新余额 → 重入漏洞!
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] = 0; // 这行永远不会执行
}
}
// 攻击合约
contract ReentrancyAttack {
VulnerableBank public bank;
function attack() external payable {
bank.deposit{value: 1 ether}();
bank.withdraw(); // 触发重入
}
receive() external payable {
if (address(bank).balance >= 1 ether) {
bank.withdraw(); // ★ 递归调用,在余额清零前重复提款
}
}
}
// ✅ 修复方案1: CEI模式(Checks-Effects-Interactions)
contract SafeBank {
mapping(address => uint256) public balances;
mapping(address => bool) private locked;
modifier nonReentrant() {
require(!locked[msg.sender], "Reentrant call");
locked[msg.sender] = true;
_;
locked[msg.sender] = false;
}
function withdraw() external nonReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// ✅ CEI: 先Effect(更新状态)
balances[msg.sender] = 0;
// 再Interaction(外部调用)
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
}
// ✅ 修复方案2: OpenZeppelin ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SafeBankV2 is ReentrancyGuard {
function withdraw() external nonReentrant {
// 自动防重入
}
}
2.2 跨函数重入(更难发现)
contract CrossFunctionReentrancy {
mapping(address => uint256) public shares;
function withdraw(uint256 amount) external {
require(shares[msg.sender] >= amount);
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok);
shares[msg.sender] -= amount; // 漏洞:先转账
}
function transfer(address to, uint256 amount) external {
require(shares[msg.sender] >= amount);
shares[msg.sender] -= amount;
shares[to] += amount;
}
// 攻击: withdraw回调中调用transfer→读取旧shares值→重复提款
}
三、闪电贷攻击
3.1 价格操纵
// Euler Finance攻击(损失1.97亿美元)简化版
contract LendingProtocol {
function getPrice(address token) public view returns (uint256) {
// ★ 依赖Uniswap现货价格 → 可被闪电贷操纵!
return uniswapPair.getReserves().token0 / uniswapPair.getReserves().token1;
}
function borrow(address token, uint256 amount) external {
uint256 collateral = amount * getPrice(token) * 1.5;
require(collateralTokens[msg.sender] >= collateral);
token.transfer(msg.sender, amount);
}
}
// 攻击流程:
// 1. 闪电贷借100M USDC
// 2. 用50M USDC在Uniswap上swap,拉高目标Token价格100x
// 3. 以操纵后的价格在LendingProtocol超额借出所有资产
// 4. 归还闪电贷(0.09%手续费),套利走人
// ✅ 修复:使用TWAP(时间加权平均价格)
contract SafeLending {
function getPriceTWAP(address token) public view returns (uint256) {
// Uniswap V2 TWAP: 30分钟均价,操控成本极高
(uint256 price0Cumulative, , uint32 blockTimestamp) =
uniswapPair.getReserves();
// TWAP = (currentCumulative - lastCumulative) / timeElapsed
return calculateTWAP(price0Cumulative, lastCumulative, blockTimestamp);
}
}
四、预言机操纵
// Compound攻击简化版(1.1亿美元):
// 攻击者通过操纵预言机价格,用极低成本抵押品借出超额资产
contract OracleManipulation {
// Mango Markets漏洞:操纵MNGO代币价格→无限借贷
function attack() external {
// 1. 闪电贷借大量USDC
// 2. 在Serum DEX上大买MNGO,推高价格10x
// 3. 以膨胀后的MNGO作抵押,借出所有USDC/BTC/ETH
// 4. 提走所有资产,不还贷
// 根本原因:预言机直接读取DEX现货价格
}
}
// ✅ 最佳实践:Chainlink去中心化预言机
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract SafeOracle {
AggregatorV3Interface internal priceFeed;
constructor() {
// ETH/USD: 多节点聚合+心跳检测+异常过滤
priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
}
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(updatedAt >= block.timestamp - 3600, "Stale price");
return price;
}
}
五、审计工具链实战
5.1 Slither静态分析
# 安装
pip install slither-analyzer
# 扫描重入漏洞
slither contracts/Bank.sol --detect reentrancy-eth
# 生成调用图+继承图
slither contracts/ --print call-graph
# 输出: Bank.withdraw() → msg.sender.call{value}() [EXTERNAL] [TAINTED]
# 自定义检测器(Detector)
# 写入 detectors/my_detector.py
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class UncheckedExternalCall(AbstractDetector):
ARGUMENT = "unchecked-external"
HELP = "Missing success check on external call"
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
def _detect(self):
results = []
for contract in self.compilation_unit.contracts_derived:
for function in contract.functions:
for node in function.nodes:
if node.contains_require_or_assert(): continue
# 检查.call返回值是否被检查
for ir in node.irs:
if "call" in str(ir) and "success" not in str(ir):
results.append(self.generate_result([
f"Unchecked external call in {function.name}\n"
]))
return results
# 运行自定义检测器
slither contracts/ --detect unchecked-external
5.2 Foundry模糊测试
// test/FuzzBank.t.sol
import "forge-std/Test.sol";
import "../src/Bank.sol";
contract FuzzBankTest is Test {
Bank bank;
function setUp() public {
bank = new Bank();
}
// ★ 模糊测试:随机1000次不同金额的存取
function testFuzz_DepositWithdraw(uint256 amount) public {
vm.assume(amount > 0 && amount < 1000 ether);
uint256 balanceBefore = address(bank).balance;
vm.deal(address(this), amount);
bank.deposit{value: amount}();
uint256 bal = bank.balances(address(this));
bank.withdraw();
// 不变式:取出后银行余额不变
assertEq(address(bank).balance, balanceBefore);
}
// ★ 符号执行:Foundry自动尝试所有路径
function testSymbolic_Invariant() public {
// handler + invariant检查
}
}
// 运行: forge test --fuzz-runs 10000 -vvvv
5.3 Mythril符号执行
pip install mythril
# 符号执行扫描
myth analyze contracts/Bank.sol --solv 0.8.19
# 输出示例:
# ==== Unprotected SELFDESTRUCT Instruction ====
# SWC ID: 106
# Severity: High
# Contract: VulnerableWallet
# Function: kill()
# The contract can be killed by anyone
# ==== Integer Overflow ====
# SWC ID: 101
# Severity: High
# The arithmetic operation can overflow
六、十大安全CheckList
| # | 检查项 | 工具 | 严重性 |
|---|---|---|---|
| 1 | CEI模式 | Slither+手工 | 🔴 致命 |
| 2 | 未检查.call返回值 | Slither | 🔴 高 |
| 3 | 预言机价格操纵 | 手工+监控 | 🔴 高 |
| 4 | tx.origin认证 | Slither | 🟡 中 |
| 5 | 整数溢出 | Solidity 0.8自动检查 | 🟢 低 |
| 6 | 未初始化storage指针 | Slither | 🔴 高 |
| 7 | 时间戳依赖(block.timestamp) | Mythril | 🟡 中 |
| 8 | 随机数可预测性 | Mythril | 🔴 高 |
| 9 | 访问控制缺失 | Slither | 🔴 致命 |
| 10 | Gas消耗循环 | 手工审查 | 🟡 中 |
七、总结
智能合约安全三要素:
- CEI是铁律 — Checks→Effects→Interactions顺序不能变
- 预言机不可信DEX现货 — 必须TWAP/Chainlink多源聚合
- 工具链必须全 — Slither(静态)+Foundry(模糊)+Mythril(符号)+Certora(形式化验证)
记住:代码即法律,部署即永恒。审计不是可选项,是必选项。
更多推荐
所有评论(0)