tx.originis a global variable which returns the address that created the original transaction.- It is similar to
msg.senderbut with some key differences. - Incorrect use of
tx.origincan lead to security vulnerabilities in smart contracts.
msg.senderreturns the immediate account that called the function.- It can be an external account or another contract calling said function.
- For example, if
UsercallsContract A, which then callsContract Bwithin the same transaction,msg.senderwill be equal toContract Awhen checked from withinContract B. tx.originwill be theUserregardless of where you check it from.
- There will be 2 smart contracts -
Good.solandAttack.sol. Initially, the owner ofGood.solwill be a good user. Using the attack function,Attack.solwill be able to change the owner ofGood.solto itself, thus denying the good user control ofGood.sol.
- Refer to
test/attack.jsfor the following explanation of the attack. - Initially,
addr1will deployGood.soland will be the owner. - The attacker will somehow fool the user that controls the private keys of
addr1to call theattack()function withinAttack.sol. - When the user calls
attack()withaddr1,tx.originis set toaddr1. attack()further callssetOwner()fromGood.solwhich checks to see iftx.originis indeed the owner. This istruebecause the original transaction was called byaddr1.- After verifying the owner, it sets the owner to
Attack.sol.
- Run the test locally using
npx hardhat test.
- THORChain users lost millions in $RUNE due to an attacker getting approvals using this attack vector.
- The attacker sent a fake token to user's wallets, then approved that token for sale on Uniswap which would then transfer $RUNE from the user's wallet to the attacker's wallet.
- This was possible because THORChain used
tx.originfor transfer checks instead ofmsg.sender. - Link to article about THORChain Hack #2
- This type of attack can be stopped by using
msg.senderinstead oftx.origin. - Example code:
function setOwner(address newOwner) public {
require(msg.sender == owner, "Not owner" );
owner = newOwner;
}