DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Improper Use of tx.origin for Authorization

Summary GmxProxy.sol::setPerpVault

Impact: High

Likelihood: Medium

The protocol use tx.origin to confirm the ownerablity could lead many issues
The function setPerpVault incorrectly uses tx.origin to verify ownership, making it vulnerable to phishing attacks and breaking compatibility with multisig wallets and contract-based interactions. This issue can allow an attacker to trick the contract owner into unintentionally setting an attacker-controlled vault address.

Vulnerability Details

function setPerpVault(address _perpVault, address market) external {
>>> require(tx.origin == owner(), "not owner");
require(_perpVault != address(0), "zero address");
require(perpVault == address(0), "already set");
perpVault = _perpVault;
gExchangeRouter.setSavedCallbackContract(market, address(this));
}
  • tx.origin refers to the original externally owned account (EOA) that initiated the transaction, even if the call passes through multiple smart contracts

  • This makes the function vulnerable to phishing: a malicious contract could trick the owner into executing an external call that indirectly calls setPerpVault (Tx-Origin Phishing Attack Pattern).

  • Composability issue: The function cannot be called from multisigs, DAOs, or contract-based automation.

Impact

  • Ownership validation bypass: An attacker could steal control over the vault settings if the owner unknowingly interacts with a malicious contract.

  • Funds misdirection or loss: If the vault manages assets, an attacker could redirect funds to their own address.

  • Breaks interoperability: The contract cannot be used with multisigs, governance contracts, or relayers, limiting its usability in DeFi and DAOs.

Tools Used

Manual Code Review

Recommendations

  • Replace tx.origin with msg.sender.

  • Role-Based Access Control (RBAC) use OpenZeppelin’s Ownable onlyOwner Modifier.

function setPerpVault(address _perpVault, address market) external onlyOwner {
- require(tx.origin == owner(), "not owner");
+ require(msg.sender == owner(), "not owner");
// OR
+ require(hasRole(OWNER_ROLE, msg.sender), "Not authorized");
require(_perpVault != address(0), "zero address");
require(perpVault == address(0), "already set");
perpVault = _perpVault;
gExchangeRouter.setSavedCallbackContract(market, address(this));
}
Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

invalid_tx-origin

Lightchaser: Medium-5

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.