Articles Home

Common Vulnerabilities in Solidity: Phishing with tx.origin

Apr 12, 2023

With the continuous development of blockchain technology, smart contracts have become an indispensable part of blockchain applications. However, because the development and deployment of smart contracts are public, attackers have the opportunity to exploit vulnerabilities for various phishing attacks. One common attack method is phishing based on tx.origin. Although tx.origin is an important global variable (transaction properties) in Ethereum, its use may pose security risks and give attackers an opportunity to take advantage. This article will delve into the principle, cases, and defense measures of phishing attacks based on tx.origin.

What Is tx.origin ?

First, let's understand the two common methods for verifying the sender address in Solidity:

tx.origin is a global variable in Ethereum smart contracts that contains the original Ethereum account address that initiated the contract call. In other words, if contract A calls contract B and contract B uses tx.origin, then tx.origin will be the caller Ethereum account address of contract A, not the address of contract A.

How tx.origin Conducts Phishing Attacks ?

Attackers can use tx.origin to carry out phishing attacks, which involve tricking users into clicking on a link or performing some action that results in their assets being stolen. Since tx.origin returns the address of the original caller rather than the current address being executed by the contract, attackers can deceive users into performing unexpected actions, such as sending assets to an address controlled by the attacker, by forging transactions.

Vulnerability Example

With the pre-knowledge, we believe that everyone has understood the functionality and drawbacks of tx.origin. Now, let's take a closer look at a vulnerable contract to further understand it:

        
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.13;
            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");
                }
            }
        
    

Vulnerability Analysis

The Wallet contract is a contract wallet where the creator can transfer their Ether into the contract upon deployment. When you need to spend money, you can call the Wallet.transfer() function to transfer any amount of funds. However, not everyone can access the funds in the contract; verification through tx.origin == owner is required to transfer funds. The issue arises here, as previously mentioned, tx.origin reads the original address of the transaction initiator. This means that we can create a phishing contract to trick the victim into initiating a transaction, thereby stealing their identity and transferring the funds in their wallet. Next, let's take a look at how the phishing contract completes the identity theft.

(Tips: Actually, there is another vulnerability in the Wallet contract, which is the reentrancy vulnerability. Here we will briefly mention that when the fallback function is called, tx.origin is still the EOA address of the initial caller. However, we will not go into further details here.)

The following is the Attack contract deployed by the attacker:

        
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.13;
            contract Attack {
                address payable public owner;
                wallet payable public owner;
                wallet wallet;

                constructor(wallet _wallet) {
                    wallet = walet(_wallet);
                    
                    owner = payable(msg.sender);
                }

                function attack() public {
                    wallet.transfer(owner,address(wallet).balance);
                }
            }
        
    

Let's analyze the attack process:

First, the victim transfers a certain amount of assets to the contract and uses it as their own contract wallet. The attacker then discovers the money in the wallet, deploys an Attack contract, and passes in the address of the Wallet contract in the constructor.

The attacker sets up a phishing website and tricks the victim into signing the malicious transaction that calls Attack.attack(). Attack.attack() called Wallet.transfer() and passed in a new owner, victim's EOA address, as well as the Wallet contract’s balance. Since the address that signed the transaction is the victim's EOA address, tx.origin for the Wallet contract is also the victim's EOA address. Therefore, the attacker successfully uses phishing to impersonate the victim's identity, passes the permission check, and successfully transfers the assets in the Wallet contract to their own account.

What Precautions Are There?