1. 문제

그냥 스위치만 뒤집으면 돼. 그렇게 어렵지 않지, 맞지?

도움될 만한 정보
CALLDATA가 어떻게 인코딩되는지 이해하는 것
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Switch {
    bool public switchOn; // switch is off
    bytes4 public offSelector = bytes4(keccak256("turnSwitchOff()"));

    modifier onlyThis() {
        require(msg.sender == address(this), "Only the contract can call this");
        _;
    }

    modifier onlyOff() {
        // we use a complex data type to put in memory
        bytes32[1] memory selector;
        // check that the calldata at position 68 (location of _data)
        assembly {
            calldatacopy(selector, 68, 4) // grab function selector from calldata
        }
        require(selector[0] == offSelector, "Can only call the turnOffSwitch function");
        _;
    }

    function flipSwitch(bytes memory _data) public onlyOff {
        (bool success,) = address(this).call(_data);
        require(success, "call failed :(");
    }

    function turnSwitchOn() public onlyThis {
        switchOn = true;
    }

    function turnSwitchOff() public onlyThis {
        switchOn = false;
    }
}

2. 코드 분석

function turnSwitchOn() public onlyThis {
    switchOn = true;
}

switchOn = true 로 변경해주는 turnSwitchOn() 함수가 존재하며, 이 함수는 onlyThis modifier가 있다.

modifier onlyThis() {
    require(msg.sender == address(this), "Only the contract can call this");
    _;
}

onlyThis() 는 컨트랙트 자기 자신만 호출 할 수 있도록 되어 있어 turnSwitchOn() 를 호출한다고 해서 문제가 풀릴 거 같지는 않다.

function flipSwitch(bytes memory _data) public onlyOff {
    (bool success,) = address(this).call(_data);
    require(success, "call failed :(");
}

flipSwitch() 함수를 살펴보면 call을 이용해서 _data에 있는 값을 실행시키는 것을 볼 수 있는데 이 부분에 turnSwitchOn() 함수를 넘겨주면 될 것 같다.

modifier onlyOff() {
    // we use a complex data type to put in memory
    bytes32[1] memory selector;
    // check that the calldata at position 68 (location of _data)
    assembly {
        calldatacopy(selector, 68, 4) // grab function selector from calldata
    }
    require(selector[0] == offSelector, "Can only call the turnOffSwitch function");
    _;
}

이제 onlyOff()만 우회하면 문제가 풀릴 것 같다.

onlyOff()를 살펴보면

  1. calldatacopy()