Understanding Phishing with tx.origin in Solidity

Posted on

Phishing attacks have become a significant concern in the digital world, and it is essential to take measures to protect against them. In the blockchain industry, Solidity is the most popular language for developing smart contracts, but it is also vulnerable to phishing attacks. In this article, we will explain how tx.origin works and how it can be used to execute phishing attacks.

What is tx.origin?

Tx.origin is a global variable in Solidity that returns the address of the original external owned account (EOA) that started the transaction. It is different from msg.sender, which returns the immediate account (external or contract account) that invoked certain function.

If there are multiple function invocations along different contracts in certain chain of transactions, tx.origin will always refer to the EOA that initiated it, no matter the stack of contracts involved, while msg.sender will refer to the last instance (EOA or smart contract) from which each function in that chain of transactions was called.

An example could be useful to understand this more clearly.
Let there be a scenario where there is an EOA which starts a transaction towards contract A, whose execution involves invoking a function of contract B. From the contract A perspective, the EOA would be the tx.origin and the msg.sender of the first invocation. However, while contract B will still see the EOA as the tx.origin , it will see contract A as the msg.sender of its method invocation.

Phishing with tx.origin

On phishing attacks using tx.origin, the attacker contract deceives the owner of a vulnerable contract into performing actions that only owners should be able to perform.

The attacker could be a contract disguised as a wallet where the receive() method includes some malicious code. Thus, if vulnerable contract owner (attacked EOA) sends ether to this malicious contract, it could end up invoking some critical method where tx.origin is used for checking the validity of the caller. This way, the attacker would succeed as tx.origin would refer to the attacked EOA, preventing the vulnerable contract from reverting as it would never know that it is actually being invoked by a smart contract and not by the owner. This can result in the theft of funds, private keys, or other sensitive data.

See also  The magic of BEM (Block, Element, Modifier)

Let’s take the following example.

VictimContract is a smart contract where only the owner should be able to withdraw its funds. VictimContract.withdrawFunds(address to) uses tx.origin to verify that caller is the owner.

Attacker is the malicious contract that will be used by the attacker.

pragma solidity ^0.8.0;
contract VictimContract {
address public owner;

constructor() {
owner = msg.sender;
}

receive() external payable{}

function withdrawFunds(address to) public {
require(tx.origin == owner);
uint contractBalance = address(this).balance;
(bool suceed,)=to.call{value:contractBalance}("");
require(suceed,"Failed withdrawal");
}

}

contract Attacker {
address public owner;
VictimContract victim; 
constructor(VictimContract _victim) {
owner = payable(msg.sender);
victim = VictimContract(_victim)
}

receive() external payable{
victim.withdrawFunds(owner); 
}

}

EOA victim1 deploys VictimContract and sends 20 Ether to it.

EOA attacker1 deploys Attacker and sets VictimContract address in its constructor.

attacker1 decieves victim1 into sending some ether to its contract, ending up in victim1 calling withdrawFunds with attacker1 as the receiver.

This way, attacker1 successfully stole 20 Ether from victim1.

Preventing tx.origin Phishing Attacks

To prevent tx.origin phishing attacks, msg.sender should be used instead of tx.origin for authentication purposes. Additionally, users should be cautious when interacting with unknown contracts and should only provide sensitive information to trusted sources.

By changing the require statement inside withdrawFunds(address to) method, the above mentioned attacked would be impossible as contract will revert as msg.sender would be referring to Attacker Contract.

pragma solidity ^0.8.0;
contract VictimContract {
address public owner;

constructor() {
owner = msg.sender;
}

receive() external payable{}

function withdrawFunds(address to) public {
require(msg.sender == owner); 
uint contractBalance = address(this).balance;
(bool suceed,)=to.call{value:contractBalance}("");
require(suceed,"Failed withdrawal");
}

}

Conclusion

Proper security measures can prevent devastating phishing attacks that utilize tx.origin. By using msg.sender instead of tx.origin when validating method callers, contracts can protect against this type of attack. It is essential to stay informed about the latest security threats and to take proactive steps to protect against them.

This does not mean that tx.origin variable should never be used, as there could be some scenarios where it could be required. For example, for assuring that a contract is not called from an smart contract, tx.origin would be useful as it always refers to an EOA.

Posted in Blockchain, Smart Contract, Solidity, TechnologiesTagged , ,

Martin Liguori
linkedin logo
twitter logo
instagram logo
By Martin Liguori
I have been working on IT for more than 20 years. Engineer by profession graduated from the Catholic University of Uruguay, and I believe that teamwork is one of the most important factors in any project and/or organization. I consider having the knowledge both developing software and leading work teams and being able to achieve their autonomy. I consider myself a pro-active, dynamic and passionate person for generating disruptive technological solutions in order to improve people's quality of life. I have helped companies achieve much more revenue through the application of decentralized disruptive technologies, being a specialist in these technologies. If you want to know more details about my educational or professional journey, I invite you to review the rest of my profile or contact me at martin@infuy.com