合约审计中的拒绝服务攻击:从“意外中止”到“永久锁死”的深度狩猎指南

admin 2026-03-03 06:17:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文深度解析智能合约拒绝服务攻击,归纳UnexpectedRevert、BlockGasLimit等6大类核心手法。结合Nomad与PlumeNetwork真实案例,揭示了资金锁死、功能瘫痪的漏洞原理,并提出采用Pull模式、限制循环次数、部署多签时间锁等防御策略。文章还提供了Slither与Foundry自动化检测工具链的实战应用,指导审计人员有效识别并修复风险,防止业务停摆。 综合评分: 92 文章分类: 代码审计,漏洞分析,区块链安全,解决方案,安全工具


cover_image

合约审计中的拒绝服务攻击:从“意外中止”到“永久锁死”的深度狩猎指南

原创

梦到什么写什么 梦到什么写什么

逍遥子讲安全

2026年2月25日 00:30 广东

一个合约功能的永久瘫痪,往往只因为一个被忽略的require——资金被锁,用户无法操作,而攻击者甚至不需要获利,只为证明“我能让你停下”。

2023年,某DeFi协议因拒绝服务漏洞导致价值1200万美元的用户资金被锁定长达6个月,最终只能通过合约升级强制解冻——而这原本可以通过一行require避免。更夸张的案例来自跨链桥Nomad:攻击者利用逻辑缺陷,仅用0.1 ETH就阻塞了整个跨链通道数小时,造成数百万美元的交易延迟。

拒绝服务(Denial of Service,DoS)攻击在合约审计中被称为“沉默的杀手”——它不直接窃取资金,但能让核心功能瘫痪、资金锁死、业务停摆。本文将完整拆解合约DoS攻击的6大类核心手法9个真实审计案例以及自动化检测工具链,全是干到拧不出水的干货。

第一章 重新认知拒绝服务攻击:为什么它是合约审计的“必查项”

1.1 DoS攻击的本质

拒绝服务攻击的核心目标是使智能合约无法正常执行预期功能。在区块链环境下,这意味着:

  • 用户无法提取资金
  • 核心业务函数执行失败
  • 合约进入永久不可用状态
  • 需要特权干预甚至重新部署才能恢复

核心认知:DoS攻击不一定要“赚钱”,它往往是组合攻击的前置步骤,或是直接破坏业务的“精确打击”。

1.2 合约DoS攻击的6大类型

| 类型 | 攻击手法 | 典型案例 | 危害等级 | | — | — | — | — | | Unexpected Revert | 利用外部调用失败阻塞关键流程 | 拍卖合约退款失败 | ⭐⭐⭐⭐ | | Block Gas Limit | 数组循环导致Gas耗尽 | 批量退款、遍历数组 | ⭐⭐⭐⭐⭐ | | Owner Action | 中心化权限滥用或丢失 | 管理员私钥丢失 | ⭐⭐⭐ | | Gas Griefing | 低代价耗尽对手Gas | 垃圾交易攻击 | ⭐⭐⭐ | | Block Stuffing | 填充区块阻止合法交易 | DEX抢跑攻击 | ⭐⭐⭐⭐ | | Cross-Batch Inconsistency | 跨批次状态不一致 | 收益分配冻结 | ⭐⭐⭐⭐⭐ |

第二章 Unexpected Revert:最经典的DoS攻击手法

2.1 漏洞原理

当合约执行外部调用(如转账、回调)时,如果调用失败且未正确处理,整个交易可能回滚,导致关键功能被阻塞。

核心问题

  • 使用send()transfer()且未检查返回值
  • 外部调用的成功与否直接影响主流程
  • 攻击者可以构造恶意合约主动拒绝接收

2.2 实战案例:拍卖合约的永久瘫痪

漏洞合约(存在DoS风险):

contract AuctionHouse {    address currentBidder;    uint256 highestBidAmount;
    function placeBid() public payable {        require(msg.value > highestBidAmount, "Bid must be higher");
        // 关键问题:将退款和主流程耦合        require(payable(currentBidder).send(highestBidAmount),                 "Failed to send Ether to previous bidder");
        currentBidder = msg.sender;        highestBidAmount = msg.value;    }}

攻击合约

soliditycontract MaliciousBidder {    AuctionHouse auctionHouse;    constructor(AuctionHouse _auctionHouse) {        auctionHouse = _auctionHouse;    }    // 没有 receive/fallback 函数,故意拒绝接收ETH    function placeMaliciousBid() public payable {        auctionHouse.placeBid{value: msg.value}();    }}

攻击流程

  1. 正常用户A出价3 ETH成为最高出价者
  2. 正常用户B出价5 ETH,A收到退款,B成为新最高者
  3. 攻击者用MaliciousBidder出价7 ETH,成为最高者,B收到退款(正常)
  4. 后续任何用户出价时,合约尝试退款给攻击者合约 → 失败 → 交易回滚
  5. 结果:拍卖合约永久瘫痪,无法接受新出价

2.3 漏洞变种:多轮退款场景

solidity// 更危险的变种:批量退款function&nbsp;refundAll() external onlyOwner {&nbsp; &nbsp;&nbsp;for(uint i =&nbsp;0; i < refundRecipients.length; i++) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 任意一个退款失败,整个交易回滚,所有用户都被卡住&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require(refundRecipients[i].send(refundAmounts[i]));&nbsp; &nbsp; }}

问题:只要有一个用户恶意拒绝接收(如合约无fallback),所有用户的退款都被阻塞。

2.4 审计检测技巧

静态分析重点

  • 搜索send()transfer()call.value()等外部调用
  • 检查返回值是否被验证
  • 识别退款操作是否与主流程耦合

动态测试思路

solidity// 测试恶意合约接收ETH失败的情况contract&nbsp;Attacker&nbsp;{&nbsp; &nbsp;&nbsp;// 故意不实现receive()&nbsp; &nbsp;&nbsp;function&nbsp;attack() external payable {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 触发目标合约的退款操作&nbsp; &nbsp; }}

第三章 Block Gas Limit:循环遍历的“隐形杀手”

3.1 漏洞原理

每个以太坊区块有Gas上限(目前约3000万)。如果一个函数执行的Gas消耗超过区块Gas限制,交易将失败。

高危场景

  • 遍历动态数组(用户数量增长后,Gas超限)
  • 循环内执行复杂操作(如多次外部调用)
  • 依赖外部状态变化的批量操作

3.2 实战案例:批量退款函数的陷阱

漏洞代码

solidityaddress[]&nbsp;private&nbsp;investors;mapping(address =>&nbsp;uint)&nbsp;public&nbsp;refundAmounts;function&nbsp;distributeRefunds() external onlyOwner&nbsp;{&nbsp; &nbsp;&nbsp;// 问题:遍历长度未知的数组&nbsp; &nbsp;&nbsp;for(uint&nbsp;i =&nbsp;0; i < investors.length; i++) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 更糟:每次迭代都进行外部调用&nbsp; &nbsp; &nbsp; &nbsp; (bool&nbsp;success, ) = investors[i].call{value: refundAmounts[investors[i]]}("");&nbsp; &nbsp; &nbsp; &nbsp; require(success,&nbsp;"refund failed");&nbsp; &nbsp; }}

攻击向量

  1. 项目早期只有10个投资者,distributeRefunds正常运行
  2. 项目发展后投资者增加到1000人
  3. distributeRefunds的Gas消耗超过区块限制
  4. 结果:退款功能永久失效,资金被锁

3.3 Immunefi真实案例:Plume Network收益分配DoS

漏洞详情:ArcToken合约的distributeYieldWithLimit函数在处理收益分配时,需要遍历所有持有人重新计算effectiveTotalSupply

solidity// 漏洞代码片段for&nbsp;(uint256&nbsp;i&nbsp;=&nbsp;0; i < totalHolders; i++) {&nbsp; &nbsp;&nbsp;address&nbsp;holder&nbsp;=&nbsp;$.holders.at(i);&nbsp; &nbsp;&nbsp;if&nbsp;(_isYieldAllowed(holder)) {&nbsp; &nbsp; &nbsp; &nbsp; effectiveTotalSupply += balanceOf(holder);&nbsp; &nbsp; }}

问题

  • 遍历所有持有人(可能数百人)
  • 每次遍历都进行外部状态检查_isYieldAllowed
  • 随着持有人增长,Gas消耗线性增加

实际影响:某次测试中,当持有人达到200人时,该函数的Gas消耗已接近区块上限。当攻击者通过空投增加大量“垃圾持有人”后,收益分配功能彻底瘫痪。

3.4 安全变体:Pull模式解决方案

solidity// 安全模式:用户自己提取(Pull模式)mapping(address =>&nbsp;uint)&nbsp;public&nbsp;pendingRefunds;function&nbsp;claimRefund() external&nbsp;{&nbsp; &nbsp;&nbsp;uint&nbsp;amount = pendingRefunds[msg.sender];&nbsp; &nbsp; require(amount >&nbsp;0,&nbsp;"no refund");&nbsp; &nbsp; pendingRefunds[msg.sender] =&nbsp;0;&nbsp; &nbsp; (bool&nbsp;success, ) = msg.sender.call{value: amount}("");&nbsp; &nbsp; require(success,&nbsp;"transfer failed");}

优势

  • Gas成本由用户承担
  • 单个用户失败不影响整体
  • 可组合防重入设计

第四章 Owner Action:中心化权限的“单点故障”

4.1 漏洞原理

当合约的关键功能依赖于特定地址(owner、admin)的操作时,该地址的故障或恶意行为将导致服务中断。

高危场景

  • 只有owner可以触发的关键操作(提现、暂停、升级)
  • owner私钥丢失或被盗
  • owner地址被合约控制且合约逻辑错误

4.2 实战案例:单点权限导致的系统瘫痪

漏洞合约

soliditycontract SinglePointOfFailure {&nbsp; &nbsp; address&nbsp;public&nbsp;owner;&nbsp; &nbsp;&nbsp;bool&nbsp;public&nbsp;paused =&nbsp;false;
&nbsp; &nbsp;&nbsp;modifier&nbsp;onlyOwner()&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; require(msg.sender == owner,&nbsp;"not owner");&nbsp; &nbsp; &nbsp; &nbsp; _;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;function&nbsp;emergencyPause() external onlyOwner&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; paused =&nbsp;true;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;function&nbsp;emergencyResume() external onlyOwner&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; paused =&nbsp;false;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;function&nbsp;withdrawFunds() external onlyOwner&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; payable(owner).transfer(address(this).balance);&nbsp; &nbsp; }}

攻击场景

  1. owner私钥在钓鱼攻击中被盗
  2. 攻击者调用emergencyPause()使合约暂停
  3. 攻击者调用withdrawFunds()窃取资金
  4. owner恢复后无法重新获得控制权(无转移所有权机制)
  5. 结果:合约永久停摆,资金被盗

4.3 防御方案:多签与时间锁

soliditycontract MultiSigWithTimelock {&nbsp; &nbsp; address[]&nbsp;public&nbsp;owners;&nbsp; &nbsp; uint&nbsp;public&nbsp;required;&nbsp; &nbsp;&nbsp;mapping(bytes32 =>&nbsp;bool)&nbsp;public&nbsp;executed;&nbsp; &nbsp; uint&nbsp;public&nbsp;constant DELAY =&nbsp;2&nbsp;days;
&nbsp; &nbsp;&nbsp;mapping(bytes32 => uint)&nbsp;public&nbsp;queuedTransactions;
&nbsp; &nbsp;&nbsp;function&nbsp;queueTransaction(address target, uint value, bytes memory data)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;external&nbsp;onlyOwner&nbsp;returns&nbsp;(bytes32)&nbsp;&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; bytes32 txHash =&nbsp;keccak256(abi.encode(target, value, data));&nbsp; &nbsp; &nbsp; &nbsp; queuedTransactions[txHash] = block.timestamp + DELAY;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;txHash;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;function&nbsp;executeTransaction(address target, uint value, bytes memory data)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;external&nbsp;onlyOwner&nbsp;returns&nbsp;(bytes memory)&nbsp;&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; bytes32 txHash =&nbsp;keccak256(abi.encode(target, value, data));&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require(queuedTransactions[txHash] >&nbsp;0,&nbsp;"tx not queued");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require(block.timestamp >= queuedTransactions[txHash],&nbsp;"too early");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require(block.timestamp <= queuedTransactions[txHash] + DELAY,&nbsp;"tx expired");
&nbsp; &nbsp; &nbsp; &nbsp; (bool&nbsp;success, bytes memory result) = target.call{value: value}(data);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;require(success,&nbsp;"tx failed");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;result;&nbsp; &nbsp; }}

第五章 Gas Griefing与Block Stuffing:资源耗尽型攻击

5.1 Gas Griefing原理

攻击者通过少量操作,迫使受害者消耗大量Gas,使攻击在经济上不可行。

典型场景:跨链桥、荷兰拍卖、MEV竞争

5.2 TrailOfBits发现:荷兰拍卖中的Gas Griefing

漏洞描述:Reserve Protocol的荷兰拍卖机制中,攻击者可以通过发起极小额的“部分成交”来阻塞整个拍卖流程。

solidity// 漏洞代码简化function swapActive()&nbsp;public&nbsp;view returns (bool) {&nbsp; &nbsp;&nbsp;if&nbsp;(block.number != blockInitialized) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;false;&nbsp; &nbsp; }&nbsp; &nbsp; uint256 sellTokenBalance = sellToken.balanceOf(address(this));&nbsp; &nbsp;&nbsp;if&nbsp;(sellTokenBalance >= sellAmount) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;false;&nbsp; &nbsp; }&nbsp; &nbsp; uint256 minimumExpectedIn = (sellAmount - sellTokenBalance) * price / D27;&nbsp; &nbsp;&nbsp;return&nbsp;minimumExpectedIn > buyToken.balanceOf(address(this));}

攻击流程

  1. 攻击者在区块开始时创建trusted fill
  2. 执行1 wei的极小部分成交,使swapActive()返回true
  3. 整个区块内所有其他操作(包括其他用户的出价)都会被阻塞
  4. 拍卖价格随时间下降,攻击者可等待价格更低时再出价
  5. 经济模型缺陷:阻塞1个区块的成本(Gas费)可能低于价格下降带来的收益

5.3 Block Stuffing:填充区块阻止合法交易

攻击原理:攻击者发送大量高Gas费交易,填满区块Gas上限,使其他合法交易无法被打包。

真实案例:某DEX在IDO期间,攻击者连续发送高Gas交易填充5个区块,导致超过2000名用户的参与交易失败,攻击者趁机抢购大量代币。

5.4 防御策略

  1. 动态Gas定价:根据网络拥堵调整关键操作的Gas要求
  2. 最小交易金额:设置合理的最小参与门槛
  3. 时间加权机制:避免瞬时价格剧烈波动
  4. MEV保护:使用私有内存池或暗池

第六章 跨批次状态不一致:新型DoS攻击手法

6.1 漏洞原理

当合约在处理批量操作时,如果中间状态发生变化,可能导致后续批次计算错误,最终因资金不足而回滚。

6.2 Immunefi审计案例:Plume Network收益分配DoS

漏洞ID:#52468,2025年8月披露

合约逻辑

  1. 收益分配分多个批次进行,每次处理部分用户
  2. 总收益金额totalAmount在第一个批次固定
  3. 每批次遍历所有持有人重新计算effectiveTotalSupply
  4. 用户份额 = (totalAmount * holderBalance) / effectiveTotalSupply

漏洞触发

  • 第一批次:处理用户A和B,effectiveTotalSupply=1000,分配840 USDC
  • 中间状态变化:管理员将用户B加入黑名单(不再享受收益)
  • 第二批次:处理用户C和D,effectiveTotalSupply变为700(少了B的200)
  • totalAmount仍然是1200
  • 计算结果:C和D应得份额超过剩余资金
  • ERC20InsufficientBalance回滚,收益分配永久停止

PoC关键代码

solidityfunction&nbsp;testDoSVulnerability()&nbsp;public&nbsp;{&nbsp; &nbsp;&nbsp;// Step 1: 第一批次处理A和B&nbsp; &nbsp; arcToken.distributeYieldWithLimit(1200,&nbsp;0,&nbsp;2);
&nbsp; &nbsp;&nbsp;// Step 2: 管理员将B加入黑名单(状态变化)&nbsp; &nbsp; yieldRestrictions.addToBlacklist(bob);
&nbsp; &nbsp;&nbsp;// Step 3: 第二批次处理C和D → 预期回滚&nbsp; &nbsp; vm.expectRevert();&nbsp;// ERC20InsufficientBalance&nbsp; &nbsp; arcToken.distributeYieldWithLimit(1200,&nbsp;2,&nbsp;2);}

6.3 整数溢出/下溢导致的永久DoS

Immunefi案例:#48998,Algorand智能合约

python# 漏洞代码(PyTeal)def&nbsp;remove_item(self, item):&nbsp; &nbsp;&nbsp;# 问题:当items为空时,items.length - 1下溢&nbsp; &nbsp;&nbsp;last_idx&nbsp;= items.length -&nbsp;1&nbsp; #&nbsp;0&nbsp;-&nbsp;1&nbsp;=&nbsp;2^64-1&nbsp; &nbsp;&nbsp;# 导致AVM运行时错误,函数永久不可用

  • 集合为空后,remove_item函数永久失效
  • 所有依赖该集合的功能全部瘫痪
  • 需重新部署合约才能恢复

第七章 自动化检测与审计工具链

7.1 静态分析工具

| 工具 | 检测能力 | 集成方式 | | — | — | — | | Slither | 循环检测、外部调用分析 | slither . --print human-summary | | Mythril | 符号执行、Gas耗尽检测 | mythril analyze contract.sol | | Securify | 数据流分析、权限检查 | 在线版本 |

自定义检测规则示例(Slither):

python# detect_dos_loop.pyfrom&nbsp;slither.detectors.abstract_detector&nbsp;import&nbsp;AbstractDetector, DetectorClassificationclass&nbsp;DoSLoopDetector(AbstractDetector):&nbsp; &nbsp; ARGUMENT =&nbsp;'dos-loop'&nbsp; &nbsp; HELP =&nbsp;'Detect potential DoS due to unbounded loops'&nbsp; &nbsp; IMPACT = DetectorClassification.HIGH&nbsp; &nbsp; CONFIDENCE = DetectorClassification.MEDIUM
&nbsp; &nbsp;&nbsp;def&nbsp;_detect(self):&nbsp; &nbsp; &nbsp; &nbsp; results = []&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;contract&nbsp;in&nbsp;self.contracts:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;function&nbsp;in&nbsp;contract.functions:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;node&nbsp;in&nbsp;function.nodes:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;node.type&nbsp;==&nbsp;'LOOP':&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 检查循环是否依赖动态数组长度&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;self._depends_on_dynamic_array(node):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; info = [f"Potential DoS in&nbsp;{function.canonical_name}\n"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; results.append(self.generate_result(info))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;results

7.2 动态分析框架

Foundry测试模板(检测Gas Griefing):

solidity// test/DosTest.t.solcontract DosTest&nbsp;is&nbsp;Test {&nbsp; &nbsp;&nbsp;function&nbsp;testGasGriefing()&nbsp;public&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 模拟1000次调用&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(uint&nbsp;i =&nbsp;0; i <&nbsp;1000; i++) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vm.startPrank(address(uint160(i)));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; targetContract.claim(); &nbsp;// 可能消耗Gas的操作&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vm.stopPrank();&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 检查Gas消耗是否超限&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint&nbsp;gasUsed = vm.snapshotGasLastCall();&nbsp; &nbsp; &nbsp; &nbsp; assertLt(gasUsed,&nbsp;30_000_000,&nbsp;"Gas exceeds block limit");&nbsp; &nbsp; }}

7.3 GasGaugeAI:AI驱动的DoS漏洞修复

滑铁卢大学的研究团队开发了GasGaugeAI——一个基于多LLM框架的自动化DoS漏洞检测与修复工具。

核心能力

  1. 漏洞分类:识别Gas依赖型漏洞(循环、外部调用等)
  2. 测试生成:自动生成Foundry测试用例验证DoS风险
  3. 函数级修复:插入Guard条件防止Gas耗尽
  4. 迭代验证:修复后重新测试确保安全性

研究成果:在数百个真实合约的测试中,GasGaugeAI成功修复了89%的Gas耗尽型漏洞,平均减少人工审计时间70%。

第八章 防御体系:构建抗DoS的智能合约

8.1 设计原则

| 原则 | 具体措施 | 解决的问题 | | — | — | — | | Pull模式 | 用户主动提取,避免批量操作 | 批量退款、批量转账 | | Pausable | 紧急暂停机制(多签控制) | Owner权限滥用 | | 限制循环规模 | 分页处理、最大长度限制 | Gas耗尽 | | 检查返回值 | 所有外部调用验证 | Unexpected Revert | | 经济模型验证 | 攻击成本 > 潜在收益 | Gas Griefing | | 状态一致性 | 跨批次操作锁定中间状态 | 跨批次不一致 |

8.2 代码级防御示例

solidity// 安全模式:分页处理 + 推送+拉取分离contract SecureAuction {&nbsp; &nbsp;&nbsp;struct&nbsp;Bid {&nbsp; &nbsp; &nbsp; &nbsp; address bidder;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint&nbsp;amount;&nbsp; &nbsp; }
&nbsp; &nbsp; Bid[]&nbsp;public&nbsp;bids;&nbsp; &nbsp;&nbsp;uint&nbsp;constant MAX_BATCH_SIZE =&nbsp;50;&nbsp; &nbsp; mapping(address =>&nbsp;uint)&nbsp;public&nbsp;pendingRefunds;
&nbsp; &nbsp;&nbsp;function&nbsp;placeBid() external payable&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 新逻辑:不立即退款,记录待退款金额&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(bids.length >&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Bid memory lastBid = bids[bids.length -&nbsp;1];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; require(msg.value&nbsp;> lastBid.amount,&nbsp;"bid too low");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pendingRefunds[lastBid.bidder] += lastBid.amount;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; bids.push(Bid(msg.sender, msg.value));&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;function&nbsp;claimRefund() external&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint&nbsp;amount = pendingRefunds[msg.sender];&nbsp; &nbsp; &nbsp; &nbsp; require(amount >&nbsp;0,&nbsp;"no refund");&nbsp; &nbsp; &nbsp; &nbsp; pendingRefunds[msg.sender] =&nbsp;0;&nbsp; &nbsp; &nbsp; &nbsp; (bool&nbsp;success, ) = msg.sender.call{value: amount}("");&nbsp; &nbsp; &nbsp; &nbsp; require(success,&nbsp;"transfer failed");&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 分页处理批量退款&nbsp; &nbsp;&nbsp;function&nbsp;processBidsBatch(uint&nbsp;start,&nbsp;uint&nbsp;count) external onlyOwner&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; require(count <= MAX_BATCH_SIZE,&nbsp;"batch too large");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint&nbsp;end = start + count;&nbsp; &nbsp; &nbsp; &nbsp; require(end <= bids.length,&nbsp;"out of bounds");
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(uint&nbsp;i = start; i < end; i++) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 处理逻辑&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}

8.3 审计检查清单

  • 是否存在无限制的循环?(数组遍历、动态长度)
  • 外部调用的返回值是否被检查?
  • 关键函数是否只有单一权限控制?
  • 是否存在批量退款的逻辑?
  • 跨批次操作是否可能被中间状态破坏?
  • 攻击者的阻塞成本是否远大于潜在收益?
  • 是否有紧急暂停和恢复机制?
  • 权限地址是否为多签或时间锁控制?

第九章 结语:拒绝服务的终点是信任崩塌

拒绝服务攻击的精髓在于:它不偷你的钱,但让你的钱永远拿不出来

从Unexpected Revert到跨批次状态不一致,从Gas耗尽到Block Stuffing——这些攻击手法的共同点是利用合约设计中的“耦合点”:退款与主流程耦合、循环与用户数耦合、状态与计算耦合。

在审计合约时,问自己三个问题:

  1. 如果某个外部调用失败,合约还能继续吗?
  2. 如果用户数量增长10倍,这个函数还能执行吗?
  3. 如果攻击者刻意制造中间状态变化,会破坏最终结果吗?

答案,往往决定了一个合约是稳健运行还是永久停摆。

而你的审计价值,也在这三个问题的延长线上。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:逍遥子讲安全 梦到什么写什么 梦到什么写什么《合约审计中的拒绝服务攻击:从“意外中止”到“永久锁死”的深度狩猎指南》

未成年这一块的/. 网络安全文章

未成年这一块的/.

文章总结: 该文档是一篇公众号推广文章,主要提供网络安全相关的免费学习资料获取渠道,包括免杀课程、安全杂志、爆破字典、逆向课程、国家信息水平测试课程、CNVD证
评论:0   参与:  0