Analysis of the Transit Finance Exploit

4 min read

Learn how Transit Finance was exploited, resulting in a loss of assets worth $115,000.

TL;DR#

On December 20, 2023, Transit Finance was exploited on the Ethereum Mainnet as well as the BNB chain due to a smart contract vulnerability, which resulted in a loss of funds worth approximately $115,000.

Introduction to Transit Finance#

Transit is a cross-chain swap platform that integrates DEXs, aggregates transactions, and provides a one-stop cross-chain solution.

Vulnerability Assessment#

The root cause of the exploit is the lack of input validation for the pool.

Steps#

Step 1:

We attempt to analyze one of the attack transactions executed by the exploiter.

Step 2:

It can be seen on the vulnerable contract that along the swapping route, the pre-deployed fake token pair would return a false value without actually transferring any tokens to mislead the transit finance route into validating the unexpected swap.

function _executeV3Swap(ExactInputV3SwapParams calldata params) internal nonReentrant whenNotPaused returns (uint256 returnAmount) {
  require(params.pools.length > 0, "Empty pools");
  require(params.deadline >= block.timestamp, "Expired");
  require(_wrapped_allowed[params.wrappedToken], "Invalid wrapped address");
  address tokenIn = params.srcToken;
  address tokenOut = params.dstToken;
  uint256 actualAmountIn = calculateTradeFee(true, params.amount, params.fee, params.signature);
  uint256 toBeforeBalance;
  bool isToETH;
  if (TransferHelper.isETH(params.srcToken)) {
    tokenIn = params.wrappedToken;
    require(msg.value == params.amount, "Invalid msg.value");
    TransferHelper.safeDeposit(params.wrappedToken, actualAmountIn);
  } else {
    TransferHelper.safeTransferFrom(params.srcToken, msg.sender, address(this), params.amount);
  }

  if (TransferHelper.isETH(params.dstToken)) {
    tokenOut = params.wrappedToken;
    toBeforeBalance = IERC20(params.wrappedToken).balanceOf(address(this));
    isToETH = true;
  } else {
    toBeforeBalance = IERC20(params.dstToken).balanceOf(params.dstReceiver);
  }

  {
    uint256 len = params.pools.length;
    address recipient = address(this);
    bytes memory tokenInAndPoolSalt;
    if (len > 1) {
      address thisTokenIn = tokenIn;
      address thisTokenOut = address(0);
      for (uint256 i; i < len; i++) {
        uint256 thisPool = params.pools[i];
        (thisTokenIn, tokenInAndPoolSalt) = _verifyPool(thisTokenIn, thisTokenOut, thisPool);
        if (i == len - 1 && !isToETH) {
          recipient = params.dstReceiver;
          thisTokenOut = tokenOut;
        }
        actualAmountIn = _swap(recipient, thisPool, tokenInAndPoolSalt, actualAmountIn);
      }
      returnAmount = actualAmountIn;
    } else {
      (, tokenInAndPoolSalt) = _verifyPool(tokenIn, tokenOut, params.pools[0]);
      if (!isToETH) {
        recipient = params.dstReceiver;
      }
      returnAmount = _swap(recipient, params.pools[0], tokenInAndPoolSalt, actualAmountIn);
    }
  }

  if (isToETH) {
    returnAmount = IERC20(params.wrappedToken).balanceOf(address(this)).sub(toBeforeBalance);
    require(returnAmount >= params.minReturnAmount, "Too little received");
    TransferHelper.safeWithdraw(params.wrappedToken, returnAmount);
    TransferHelper.safeTransferETH(params.dstReceiver, returnAmount);
  } else {
    returnAmount = IERC20(params.dstToken).balanceOf(params.dstReceiver).sub(toBeforeBalance);
    require(returnAmount >= params.minReturnAmount, "Too little received");
  }

  _emitTransit(params.srcToken, params.dstToken, params.dstReceiver, params.amount, returnAmount, 0, params.channel);
}

Step 3:

Due to this lack of validation, the attacker was able to fake a pool and manipulate the `actualAmountIn` value of this function in the first swap.

This caused the SwapRouter to take the forged `actualAmountIn` as the initial value for swap in the next WBNB/BUSD pool, thereby making the router supply more assets than intended.

Step 4:

The stolen assets were then sent to a PancakePair created by the attacker prior to the exploit, which were later inflated and removed to take away the profits.

Step 5:

The exploiter has already laundered 36 ETH worth $78,000 and 147 BNB worth $37,300 to Tornado Cash.

Aftermath#

The team didn't directly acknowledge the occurrence of the exploit on their social media handle across X but commented that the user assets were unaffected and that they had completed the contract upgrade.

Solution#

In addressing the vulnerabilities suffered by the Transit Finance exploit, several measures can be implemented to enhance the security and robustness of DeFi platforms.

Firstly, rigorous validation of data types is crucial. Smart contracts should be designed to strictly verify the types of inputs. This prevents the misuse of functions due to incorrect or maliciously crafted data. For instance, ensuring that an address is indeed a valid token address or that numerical values do not exceed expected ranges can mitigate risks significantly.

Sanitizing user input is equally important. This involves implementing mechanisms to cleanse or reject inputs that could potentially cause unexpected or harmful outcomes. It's about anticipating the ways in which a user might interact with the contract in unintended ways and safeguarding against these scenarios. Utilizing fuzzing tools represents an advanced and proactive approach to identifying vulnerabilities. Fuzzing involves providing a wide range of random, unexpected, or invalid inputs to the system to see how it responds. This can uncover hidden bugs or vulnerabilities that would not be apparent through standard testing methods.

Developing and testing against a comprehensive set of edge cases ensures that the contract behaves correctly under all possible conditions, not just the most common or expected ones. This means deliberately creating scenarios that push the limits of the contracts' logic and observing how they handle these extreme cases.

Even with stringent security protocols, completely eradicating the risk of exploitation remains an unachievable goal. This unavoidable uncertainty highlights the vital role of comprehensive cover policies, similar to those provided by Neptune Mutual. By setting up a dedicated cover pool in our marketplace, the consequences and severity of incidents like the Transit Finance exploit could have been considerably lessened. Our offerings reassure users by reducing financial losses or digital asset losses due to vulnerabilities in smart contracts, thanks to our unique parametric-based policies.

For users who opt for our parametric cover policies, the need to submit proof of loss for a payout is eliminated. Once an incident is verified and resolved through our incident resolution system, affected parties can immediately claim their payouts.

Our marketplace is accessible on multiple established blockchain networks, including EthereumArbitrum, and the BNB chain, aiming to provide insurance coverage to a broad spectrum of DeFi participants. Our involvement in the ecosystem not only offers a safeguard for users but also strengthens their confidence in the DeFi landscape.

Reference Source BlockSec

By

Tags