Ethernaut 题库闯关 #19 — Denial

Ethernaut题库闯关连载的第19篇题解。

今天这篇是Ethernaut 题库闯关连载的第19篇,难度等级: 有点难。

欢迎大家订阅专栏:Ethernaut 题库闯关,坚持挑战下去,你的 Solidity代码能力肯定大有提高。

挑战#19 Denial(拒绝服务)

这次挑战的是一个简单的钱包合约,随着时间的推移,合约的资金会不断增加。你可以通过设置提款伙伴来慢慢提取合约资金。

我们要做的是,进行一次 DOS(拒绝服务攻击),以实现在所有者调用 withdraw()时拒绝他们提取资金(此时合约里仍有资金,不过交易的 gas 会在100万或以下)。

以下是合约代码:

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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Denial {

    using SafeMath for uint256;
    address public partner; // withdrawal partner - pay the gas, split the withdraw
    address payable public constant owner = address(0xA9E);
    uint timeLastWithdrawn;
    mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances

    function setWithdrawPartner(address _partner) public {
        partner = _partner;
    }

    // withdraw 1% to recipient and 1% to owner
    function withdraw() public {
        uint amountToSend = address(this).balance.div(100);
        // perform a call without checking return
        // The recipient can revert, the owner will still get their share
        partner.call{value:amountToSend}("");
        owner.transfer(amountToSend);
        // keep track of last withdrawal time
        timeLastWithdrawn = now;
        withdrawPartnerBalances[partner] = withdrawPartnerBalances[partner].add(amountToSend);
    }

    // allow deposit of funds
    receive() external payable {}

    // convenience function
    function contractBalance() public view returns (uint) {
        return address(this).balance;
    }
}

研究合约

让我们回顾一下合约的代码, 合约相当容易理解。其背后的想法是,partner是为调用withdraw而付清Gas费的人,每次提款操作都会得到合约余额的1%的回报。

在现实生活中,你应该计算一下进行操作的Gas费用是否值得这1%,但这不是挑战的范围的一部分。

我们唯一感兴趣的函数是withdraw,让我们仔细看看它:

// withdraw 1% to recipient and 1% to owner
function withdraw() public {
    uint256 amountToSend = address(this).balance.div(100);
    partner.call{value: amountToSend}("");
    owner.transfer(amountToSend);
    timeLastWithdrawn = now;
    withdrawPartnerBalances[partner] = withdrawPartnerBalances[partner].add(amountToSend);
}

让我们一步步看看这个函数的作用:

  • amountToSend中设置发送的金额
  • 通过一个低级别的 "调用",将余额的1%转给 partner
  • 通过transfer将余额的1%转给合约的 "所有者"。
  • 更新最后一次执行withdraw的时间
  • 更新partner已经提取的金额

正如我们所说,这...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ethernaut CTF
Ethernaut CTF
信奉 CODE IS LAW.