Let's understand what phishing actually is:
Phishing is a cybercrime in which a target or targets are contacted by email, telephone or text message by someone posing as a legitimate institution to lure individuals into providing sensitive data such as personally identifiable information, banking, and credit card details, and passwords.
The information is then used to access important accounts and can result in identity theft and financial loss.
In this post, We will try to understand how a contract owner can be fooled into transferring all their eth to another Ethereum address
pragma solidity ^0.6.10;
contract Wallet {
address public owner;
constructor() public {
owner = msg.sender;
}
function deposit() public payable {}
function transfer(address payable _to, uint amount) public {
require(tx.origin == owner, "Not the owner of this contract");
(bool sent, ) = _to.call{value: amount}("");
require (sent, "Failed to send ether");
}
function getBalance() public view returns (uint){
return address(this).balance;
}
}
This contract lets any address to deposit ether into it through the deposit function. Only the owner of this contract will be able to withdraw ether from this smart contract. Lets us say, Ravya deploys this contract and other users can deposit any amount of ether into this contract.
So in principle, Only Ravya can withdraw all the ether deposited in this smart contract.
This contract might have other functions to make it more usable but for the sake of simplicity, I have put only three functions in this Wallet contract to demonstrate the phishing attack.
Let us design another contract -Phishing- to execute the phishing attack on this Wallet contract.
contract Phishing {
address payable public owner;
Wallet wallet ;
constructor(Wallet _wallet) public {
wallet = Wallet(_wallet); //setting the wallet storage
//variable to the address of the wallet
// contract deployed on the chain.
owner = msg.sender;
}
function transfer() public {
wallet.transfer(owner, address(wallet).getBalance());
}
}
Lets us see how another user -let us call him Kris- can use this contract to withdraw ether from the Wallet contract, even though he is not the owner of this Wallet contract.
The transfer() function of the Phishing contract calls the transfer function of the original Wallet contract with the owner, who deployed this contract -in this case, Kris- and the total amount of ether stored in the instance of Wallet contract.
For this phishing attack to execute successfully, Kris would have to convince Ravya to call the transfer function of the Phishing contract rather than the transfer function of the Wallet contract.
If Ravya calls the transfer function of the Phishing contract, The tx.origin will be set to Ravy’s address in the transfer function of both the contracts: Phishing and Wallet.
Consequently, This “require” statement in the Wallet contract will be executed successfully and all the ether in the Wallet contract will be transferred to the Kris address since tx.origin will be Ravya’s address even though, this function is being called from the transfer function of Phishing contract.
require(tx.origin == owner, "Not the owner of this contract");
How to stop this attack?
tx.origin gives the origin of the transactions, so the user address it was originally sent from. With tx.origin the owner can never be a contract.
In a simple call chain A->B->C->D, inside D msg.sender will be C, and tx.origin will be A.
To ward off this phishing attack, Just use msg.sender instead of tx.origin because of the following reason***:***
msg.sender gives the direct sender of the message, so for example a contract that passed it along.
So in our case, msg.sender will be the address of Phishing contract address rather than the address of Ravya which will cause require statement to fail.
