tx.origin Authentication
Detects use of tx.origin for authentication or access control, which allows phishing attacks to bypass authorization checks.
tx.origin Authentication
Overview
Remediation Guide: How to Fix tx.origin Authentication
The tx-origin detector identifies smart contracts that use tx.origin to authenticate callers or enforce access control. tx.origin always refers to the externally owned account (EOA) that originally initiated the transaction — not necessarily the immediate caller of the current function. When a contract grants privileges based on tx.origin, any intermediate contract in the call chain can exploit this by tricking the original sender into invoking a malicious intermediary, which then calls the victim contract while tx.origin still points to the victim’s trusted account.
Sigvex detects this pattern by tracking the ORIGIN opcode through the contract’s control-flow graph and flagging comparisons or conditional branches that depend on its value, particularly when used in privilege-sensitive contexts such as fund transfers, ownership checks, or state-modifying operations.
The use of tx.origin for authentication is explicitly listed in the Solidity security documentation as an anti-pattern (SWC-115). While tx.origin has legitimate uses — such as detecting whether a caller is an EOA rather than a contract — using it for authorization is never safe in a world where smart contract wallets and account abstraction (ERC-4337) are prevalent.
Why This Is an Issue
A phishing attack exploiting tx.origin authentication works as follows: the victim owns a contract that uses require(tx.origin == owner). An attacker deploys a malicious contract and tricks the victim into calling it (e.g., via a fake token claim). The malicious contract then calls the victim’s protected function. Because tx.origin still points to the victim’s EOA, the authentication check passes and the attacker gains unauthorized access.
Beyond phishing, account abstraction (ERC-4337) separates transaction execution from EOA signing. In ERC-4337 flows, the UserOperation is submitted by a Bundler, meaning tx.origin is the Bundler’s address rather than the user’s — completely breaking tx.origin-based authentication for smart contract wallet users.
SWC-115 has been exploited in multiple protocols. The general pattern is well understood: any contract that controls value and uses tx.origin for authorization is exploitable by social engineering the original signer.
How to Resolve
Replace tx.origin with msg.sender for authorization, and structure access control so that only the immediate caller is trusted:
// Before: Vulnerable — phishable tx.origin check
contract VulnerableWallet {
address public owner;
constructor() {
owner = msg.sender;
}
function transfer(address payable _to, uint256 _amount) external {
// VULNERABLE: any contract can call this if it tricks the owner
require(tx.origin == owner, "Not authorized");
_to.transfer(_amount);
}
}
// After: Fixed — use msg.sender for authorization
contract SafeWallet {
address public owner;
constructor() {
owner = msg.sender;
}
function transfer(address payable _to, uint256 _amount) external {
// SAFE: msg.sender must be the direct caller
require(msg.sender == owner, "Not authorized");
_to.transfer(_amount);
}
}
If you need to distinguish EOA callers from contracts (a legitimate use), use tx.origin == msg.sender:
// Legitimate use: detecting if caller is an EOA (not a contract)
function onlyEOA() internal view {
require(tx.origin == msg.sender, "Contracts not allowed");
}
Examples
Vulnerable Code
contract PhishableContract {
address public owner;
constructor() {
owner = msg.sender;
}
// Attacker creates a malicious contract that calls this
// while tx.origin is still the owner's EOA
function changeOwner(address _newOwner) public {
require(tx.origin == owner, "Not authorized");
owner = _newOwner;
}
}
// Attacker's contract:
contract MaliciousContract {
PhishableContract victim;
constructor(address _victim) {
victim = PhishableContract(_victim);
}
// Victim is tricked into calling this function
function attack() public {
// tx.origin == victim's EOA, so the check passes
victim.changeOwner(msg.sender);
}
}
Fixed Code
contract SafeContract {
address public owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _newOwner) public {
// msg.sender must be the direct caller — attacker's contract cannot spoof this
require(msg.sender == owner, "Not authorized");
owner = _newOwner;
}
}
Sample Sigvex Output
{
"detector_id": "tx-origin",
"severity": "high",
"confidence": 0.95,
"description": "Function changeOwner() uses tx.origin for authorization. An attacker can exploit this via a phishing contract that calls the function while tx.origin points to the legitimate owner.",
"location": { "function": "changeOwner(address)", "offset": 42 }
}
Detection Methodology
Sigvex decompiles bytecode and tracks the ORIGIN opcode through the contract’s HIR. The detector:
- Locates ORIGIN reads: Identifies all instructions that read the
tx.originvalue via theORIGINopcode. - Traces usage: Follows the value through assignments, comparisons, and conditional branches using data-flow analysis.
- Checks context: Flags ORIGIN usage that appears in conditional guards controlling privileged operations (transfers, ownership changes, state modifications).
- Filters legitimate uses: Reduces confidence when ORIGIN is compared directly to
msg.sender(the EOA detection pattern), which is a legitimate use. - Severity scoring: Assigns high confidence when ORIGIN guards fund transfers or ownership changes; medium confidence for informational-only usages.
Limitations
False positives:
require(tx.origin == msg.sender)is a legitimate pattern for blocking contract callers and will receive reduced confidence but may still be reported.- Some contracts use
tx.originonly in view functions or event emissions where it poses no direct exploit risk.
False negatives:
- Indirect ORIGIN usage through library calls or inline assembly may not be detected.
- Complex multi-step patterns where the ORIGIN value is stored and later used may be missed without full inter-procedural analysis.
Related Detectors
- Access Control — detects broader access control issues
- Signature Replay — detects signature-based authentication weaknesses