Stratax Contracts

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

Unvalidated Swap Recipient Allows Fund Diversion via Return Value Trust

Author Revealed upon completion

Root + Impact

Description

The protocol integrates with 1inch DEX aggregator for token swaps during position creation and unwinding. The _call1InchSwap function accepts _swapParams (encoded calldata) that calls the swap() function on the 1inch router. However, the function has two critical flaws:

  1. No Parameter Validation: The function does not verify that dstReceiver in the SwapDescription is set to address(this), allowing swap outputs to be sent to arbitrary addresses.

  2. Return Value Trust: The function trusts the return value from the swap call instead of checking the actual token balance increase. Even if tokens are sent to the wrong address, the return value may still indicate success and a high amount received.

When a malicious owner provides swap calldata with dstReceiver set to their own address, the swap executes successfully and returns a high value, but the tokens never reach the contract. The contract then proceeds with operations assuming it received the tokens, potentially using other users' funds to repay flash loans.

// src/Stratax.sol:612-629
function _call1InchSwap(bytes memory _swapParams, address _asset, uint256 _minReturnAmount)
internal
returns (uint256 returnAmount)
{
// @> No validation of swap parameters before execution
// @> Does not verify dstReceiver == address(this)
(bool success, bytes memory result) = address(oneInchRouter).call(_swapParams);
require(success, "1inch swap failed");
// Decode the return amount from the swap
if (result.length > 0) {
// @> Trusts return value without verifying actual balance increase
// @> If tokens were sent to wrong address, return value is still trusted
(returnAmount,) = abi.decode(result, (uint256, uint256));
} else {
// If no return data, check balance
returnAmount = IERC20(_asset).balanceOf(address(this));
}
// @> Only checks return amount, doesn't verify tokens actually arrived
require(returnAmount >= _minReturnAmount, "Insufficient return amount from swap");
return returnAmount;
}
// src/Stratax.sol:582-588
// Step 3: Swap collateral to debt token to repay flash loan
IERC20(unwindParams.collateralToken).approve(address(oneInchRouter), withdrawnAmount);
uint256 returnAmount = _call1InchSwap(unwindParams.oneInchSwapData, _asset, unwindParams.minReturnAmount);
// Step 4: Repay flash loan
uint256 totalDebt = _amount + _premium;
// @> Uses returnAmount which may be fake if tokens went to wrong address
require(returnAmount >= totalDebt, "Insufficient funds to repay flash loan");

Risk

Likelihood:

  • The combination of unvalidated dstReceiver parameter and return value trust creates a clear attack vector that can be exploited whenever the contract has other users' funds available.

Impact:

  • Fund Diversion: A malicious owner can redirect swap outputs to their own address by setting dstReceiver to their address in the swap description. The swap executes successfully and returns a high value, but tokens never reach the contract.

Proof of Concept

Recommended Mitigation

The most critical fix is to verify the actual token balance increase instead of trusting the return value:

function _call1InchSwap(bytes memory _swapParams, address _asset, uint256 _minReturnAmount)
internal
returns (uint256 returnAmount)
{
+ uint256 balanceBefore = IERC20(_asset).balanceOf(address(this));
+
(bool success, bytes memory result) = address(oneInchRouter).call(_swapParams);
require(success, "1inch swap failed");
- // Decode the return amount from the swap
- if (result.length > 0) {
- (returnAmount,) = abi.decode(result, (uint256, uint256));
- } else {
- // If no return data, check balance
- returnAmount = IERC20(_asset).balanceOf(address(this));
- }
+ uint256 balanceAfter = IERC20(_asset).balanceOf(address(this));
+ returnAmount = balanceAfter - balanceBefore;
+
require(returnAmount >= _minReturnAmount, "Insufficient return amount from swap");
return returnAmount;
}

This fix ensures that even if dstReceiver is set incorrectly, the attack will fail because returnAmount will be 0 (no balance increase), causing the minReturnAmount check to fail.

Support

FAQs

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

Give us feedback!