Ethernaut Level 2: Fallout
Solidity Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Fallout {
using SafeMath for uint256;
mapping (address => uint) allocations;
address payable public owner;
/* constructor */
function Fal1out() public payable {
owner = msg.sender;
allocations[owner] = msg.value;
}
modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function allocate() public payable {
allocations[msg.sender] = allocations[msg.sender].add(msg.value);
}
function sendAllocation(address payable allocator) public {
require(allocations[allocator] > 0);
allocator.transfer(allocations[allocator]);
}
function collectAllocations() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
function allocatorBalance(address allocator) public view returns (uint) {
return allocations[allocator];
}
}
Requirements
- Claim ownership of the contract.
Concepts
- Contract constructor.
Hack
Looking at the code, no other function aside from the supposed constructor
function assigns ownership of the contract. Furthermore, if you notice Fal1out()
has a typo and is a public function. In previous versions of solidity:
Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax was deprecated and is not allowed anymore in version 0.5.0.
Because of the typo, Fal1out()
is treated as a regular function instead of the constructor
. Furthermore, anyone can call Fal1out()
because it is public.
Solution
Taking ownership of the contract is very simple.
await contract.Fal1out({from: player, value: toWei("0.001")})
The value can be any arbitrary number. To check if we are the contract owner, we run:
await contract.owner()
Done!