ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Solidity智能合约安全攻防实战:从重入攻击到闪电贷完整审计

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静态分析# 安装pipinstallslither-analyzer# 扫描重入漏洞slither contracts/Bank.sol--detectreentrancy-eth# 生成调用图继承图slither contracts/--printcall-graph# 输出: Bank.withdraw() → msg.sender.call{value}() [EXTERNAL] [TAINTED]# 自定义检测器(Detector)# 写入 detectors/my_detector.pyfrom slither.detectors.abstract_detectorimportAbstractDetector, DetectorClassification class UncheckedExternalCall(AbstractDetector): ARGUMENTunchecked-externalHELPMissing success check on external callIMPACTDetectorClassification.HIGH CONFIDENCEDetectorClassification.HIGH def _detect(self): results[]forcontractinself.compilation_unit.contracts_derived:forfunctionincontract.functions:fornodeinfunction.nodes:ifnode.contains_require_or_assert():continue# 检查.call返回值是否被检查foririnnode.irs:ifcallinstr(ir)andsuccessnotinstr(ir): results.append(self.generate_result([fUnchecked external call in {function.name}\n]))returnresults# 运行自定义检测器slither contracts/--detectunchecked-external5.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 -vvvv5.3 Mythril符号执行pipinstallmythril# 符号执行扫描myth analyze contracts/Bank.sol--solv0.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#检查项工具严重性1CEI模式Slither手工 致命2未检查.call返回值Slither 高3预言机价格操纵手工监控 高4tx.origin认证Slither 中5整数溢出Solidity 0.8自动检查 低6未初始化storage指针Slither 高7时间戳依赖(block.timestamp)Mythril 中8随机数可预测性Mythril 高9访问控制缺失Slither 致命10Gas消耗循环手工审查 中七、总结智能合约安全三要素CEI是铁律— Checks→Effects→Interactions顺序不能变预言机不可信DEX现货— 必须TWAP/Chainlink多源聚合工具链必须全— Slither(静态)Foundry(模糊)Mythril(符号)Certora(形式化验证)记住:代码即法律,部署即永恒。审计不是可选项,是必选项。
返回列表