Stratax Contracts

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

Unrestricted recoverTokens allows complete drainage of positions

Author Revealed upon completion

Root + Impact

The recoverTokens() function lacks any restrictions on which tokens can be recovered, allowing the contract owner to withdraw aTokens (representing supplied collateral) and debt tokens directly from the contract without authorization or validation. Complete loss of user position value through unauthorized withdrawal of Aave collateral tokens and potential forced liquidation via debt token manipulation.

Description

The recoverTokens() function in Stratax.sol is designed as an emergency recovery mechanism for tokens mistakenly sent to the contract. However, it lacks critical safeguards:

function recoverTokens(address _token, uint256 _amount) external onlyOwner {
@> IERC20(_token).transfer(owner, _amount);
}

Risk

Any honest user with an active leveraged position risks complete loss of their collateral via recoverTokens(). No specialized exploit is needed; the owner simply calls a public function with parameters easily visible on-chain. Users cannot prevent this, and there is no recovery mechanism and this is a straightforward loss-of-funds scenario driven by normal protocol operation and malicious or compromised ownership

Impact

  • Users lose 100% of supplied collateral

  • Removing aTokens makes positions undercollateralized; Aave liquidators can seize remaining assets

Proof of Concept

  1. Alice creates a 3x leveraged ETH position, providing 10 ETH collateral and borrowing 20 ETH via flash loan.

  2. The Stratax contract supplies all 30 ETH to Aave as collateral and receives 30 aWETH in return. Alice's position is now live with a healthy health factor (> 1.5).

  3. The contract owner (or an attacker after key compromise) calls recoverTokens(aWethAddress, 30e18).

  4. All 30 aWETH are transferred to the owner's wallet. Stratax contract balance of aWETH becomes zero.

  5. Alice's position becomes undercollateralized; health factor drops to ~0.3 (liquidatable).

  6. Aave liquidators immediately liquidate the position. Alice loses her entire 10 ETH principal plus accrued interest debt, and the position is wiped from the protocol

function test_recoverTokensDrainsCollateral() public {
vm.startPrank(user);
weth.approve(address(stratax), 100e18);
stratax.createLeveragedPosition(
address(weth),
100e18,
100e18,
address(usdc),
1_000_000e6,
oneInchSwapData,
minReturnAmount
);
vm.stopPrank();
uint256 aWethBalance = aToken.balanceOf(address(stratax));
assertEq(aWethBalance, 200e18, "Position should have 200 aWETH");
vm.prank(owner);
stratax.recoverTokens(address(aToken), aWethBalance);
assertEq(aToken.balanceOf(address(stratax)), 0, "All aWETH should be drained");
vm.prank(user);
vm.expectRevert();
stratax.unwindPosition(
address(weth),
100e18,
address(usdc),
1_000_000e6,
oneInchSwapData,
minReturnAmount
);
}

Recommended Mitigation

  • Restrict recoverable tokens to a whitelist of tokens that are not position-related (e.g. not aTokens, not variable/stable debt tokens).

Support

FAQs

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

Give us feedback!