Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: medium
Likelihood: medium

Single-Step Ownership Transfer Can Irreversibly Misconfigure Admin Control

Author Revealed upon completion

Single-Step Ownership Transfer Can Irreversibly Misconfigure Admin Control


Description

Both StrataxOracle and Stratax implement transferOwnership as an immediate, one-transaction ownership change.

In StrataxOracle, ownership is reassigned directly:

function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "Invalid address");
address previousOwner = owner;
@> owner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}

In Stratax, the same pattern is used:

function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "Invalid address");
@> owner = _newOwner;
}

Because ownership transfer does not require acceptance by the new owner, any mistake in _newOwner (wrong address, typo, stale deployment address, non-controlled contract, compromised routing flow) becomes final as soon as the transaction is mined.

If ownership is accidentally transferred to an address that cannot or will not operate admin functions, privileged operations become permanently unavailable to the intended operator. This creates governance and operational fragility for critical protocol controls.

In this protocol, owner-only functionality includes:

  • StrataxOracle: setPriceFeed, setPriceFeeds, transferOwnership

  • Stratax: setStrataxOracle, setFlashLoanFee, recoverTokens, unwindPosition, transferOwnership

As a result, a single operational mistake during ownership rotation can block oracle maintenance, fee configuration, token recovery, and position management.


Risk

Likelihood: Medium

This issue is typically triggered by operational error rather than direct external exploitation. However, ownership transfers are high-impact administrative actions, and address-entry mistakes or process misconfigurations are realistic in production operations.

Impact: Medium

If ownership is transferred to an incorrect or unusable address, the protocol can lose effective administrative control over critical parameters and recovery flows. This can degrade incident response and protocol maintenance, and in some scenarios lead to prolonged downtime or locked funds requiring migration.


Proof of Concept

Below is the full PoC code with two tests (for Stratax and StrataxOracle):

contract OwnershipTransferRiskTest is Test {
Stratax internal stratax;
StrataxOracle internal oracle;
address internal realAdmin;
address internal wrongAddress;
function setUp() public {
realAdmin = makeAddr("realAdmin");
wrongAddress = makeAddr("wrongAddress");
oracle = new StrataxOracle();
stratax = new Stratax();
stratax.initialize(
address(0x1001), // aavePool
address(0x1002), // aaveDataProvider
address(0x1003), // oneInchRouter
address(0x1004), // usdc
address(oracle) // strataxOracle
);
}
function test_Stratax_OneStepOwnershipTransferMisconfiguration() public {
// Deployer gives control to intended admin.
stratax.transferOwnership(realAdmin);
// Intended admin makes an operational mistake and sets a wrong address.
vm.prank(realAdmin);
stratax.transferOwnership(wrongAddress);
// Intended admin immediately loses owner-only permissions.
vm.prank(realAdmin);
vm.expectRevert("Not owner");
stratax.setFlashLoanFee(25);
// Wrong address now controls owner-only functionality.
vm.prank(wrongAddress);
stratax.setFlashLoanFee(25);
assertEq(stratax.flashLoanFeeBps(), 25);
}
function test_StrataxOracle_OneStepOwnershipTransferMisconfiguration() public {
address token = address(0x2001);
address feed = address(0x2002);
vm.mockCall(feed, abi.encodeWithSignature("decimals()"), abi.encode(uint8(8)));
// Deployer gives control to intended admin.
oracle.transferOwnership(realAdmin);
// Intended admin mistakenly transfers ownership to a wrong address.
vm.prank(realAdmin);
oracle.transferOwnership(wrongAddress);
// Intended admin immediately loses owner-only permissions.
vm.prank(realAdmin);
vm.expectRevert("Not owner");
oracle.setPriceFeed(token, feed);
// Wrong address can now perform owner-only oracle updates.
vm.prank(wrongAddress);
oracle.setPriceFeed(token, feed);
assertEq(oracle.priceFeeds(token), feed);
}
}

Run the test with:

forge test --match-path test/unit/OwnershipTransfer.t.sol

Output:

Ran 2 tests for test/unit/OwnershipTransfer.t.sol:OwnershipTransferRiskTest
[PASS] test_StrataxOracle_OneStepOwnershipTransferMisconfiguration() (gas: 57668)
[PASS] test_Stratax_OneStepOwnershipTransferMisconfiguration() (gas: 32336)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 10.06ms (1.42ms CPU time)
Ran 1 test suite in 137.59ms (10.06ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)

As shown by the output, a single mistaken transferOwnership call immediately removes the intended admin's permissions and transfers control to the mistakenly provided address.


Recommended Mitigation

Use a two-step ownership transfer flow (pendingOwner + acceptOwnership) in both contracts.

For StrataxOracle, for example:

address public owner;
+ address public pendingOwner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+ event OwnershipTransferStarted(address indexed previousOwner, address indexed pendingOwner);
function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "Invalid address");
- address previousOwner = owner;
- owner = _newOwner;
- emit OwnershipTransferred(previousOwner, _newOwner);
+ pendingOwner = _newOwner;
+ emit OwnershipTransferStarted(owner, _newOwner);
+ }
+
+ function acceptOwnership() external {
+ require(msg.sender == pendingOwner, "Not pending owner");
+ address previousOwner = owner;
+ owner = pendingOwner;
+ pendingOwner = address(0);
+ emit OwnershipTransferred(previousOwner, owner);
}

Apply the same pattern to Stratax and its interface so ownership changes are completed only after explicit acceptance by the recipient.

If desired, this can be replaced with OpenZeppelin Ownable2StepUpgradeable semantics for standardized behavior and reduced custom access-control risk.

Support

FAQs

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

Give us feedback!