The lack of token addresses' verification
Description
The permitSwapAndBridge
and swapAndBridge
functions allow users to perform the swap, and after that, bridge the resulting tokens to another chain using Stargate, which is a decentralised bridge and exchange building on top of the Layer Zero protocol.
These functions have the following parameters: swapPayload
, receivedToken
, and _srcPoolId
. The swapPayload
parameter contains all the necessary data for the swap, including the path
array with the list of tokens involved in the swap process. It is assumed that the final address of the token participating in the swap will be used for cross-chain swap.
The receivedToken
token address will be used by the _bridge
function for assigning the approval for the stargateRouter contract.
Besides that, the user controls the _srcPoolId
parameter, which determines the pool
address, which is associated with a specific token and will hold the assets of the tokens that will be transferred from the current contact inside stargateRouter. However, there is no verification that these three addresses -- receivedToken
, the last address in path
and pool.token()
-- match each other.
When using native tokens, the user should pass the wethAddress
address as receivedToken
because before _bridge
, the necessary amount of tokens should be withdrawn from the weth contract. After that, the receivedToken
will be rewritten to zero address. Currently there is no verification that the receivedToken
is not zero initially.
function swapAndBridge(
uint amountIn,
address fromToken,
address receivedToken,
uint16 srcPoolId,
uint16 dstPoolId,
bytes1 dexId,
bytes calldata swapPayload,
bytes calldata bridgePayload
) external payable {
_checkConditions(amountIn);
ERC20(fromToken).safeTransferFrom(msg.sender, address(this), amountIn);
uint256 receivedAmount;
if (dexId != 0x05) {
receivedAmount = _swap(dexId, amountIn, swapPayload);
} else {
ERC20(fromToken).safeApprove(balancerVault, amountIn);
receivedAmount = _swapBalancer(swapPayload);
}
if (receivedToken == wethAddress) {
WETH(wethAddress).withdraw(receivedAmount);
receivedToken = address(0);
}
_bridge(
receivedAmount,
receivedToken,
srcPoolId,
dstPoolId,
bridgePayload
);
}
function _bridge(
uint amountIn,
address fromToken,
uint16 srcPoolId,
uint16 dstPoolId,
bytes calldata payload
) private {
if (fromToken == address(0)) {
/* NOTE: If sending after swap to ETH then msg.value will be < amountIn as it only contains the fee
If sending without swap msg.value will be > amountIn as it contains both fee + amountIn
**/
uint256 msgValue = msg.value > amountIn
? msg.value
: amountIn + msg.value;
IStargateRouter(stargateRouterEth).swapETHAndCall{value: msgValue}(...);
...
}
...
}
Impact
Due to the lack of verification that the receivedToken
address matches the last address in the path
array and pool.token()
address, users are able to employ any token address as the receivedToken
. This potentially allows them to successfully execute cross-chain swaps using tokens owned by the contract.
In instances where a user initially sets the receivedToken
address to the zero address, the required amount of tokens will not be withdrawn from the weth contract. Consequently, the contract will attempt to transfer to the stargateRouter contract the funds present in its balance before the transaction took place.
In both scenarios, if the contract possesses any tokens, they can be utilized instead of the tokens received during the execution of the swap.
This also leads to a problem in the _bridge
function during msgValue
calculation. When the fromToken
(receivedToken
from swapAndBridge
) is not the outcome of a swap, users can specify any amountIn
as the result of a swap involving a different token. This amountIn
value will then be used as the ETH value. Consequently, if the contract holds other funds, they will be sent to stargateRouterEth along with the user's fee.
Recommendations
We recommend to add the check that the receivedToken
and the last address in path
and pool.token()
match each other --- and also that the receivedToken
address is not equal to zero address.
Remediation
This issue has been acknowledged by Y2K Finance, and a fix was implemented in commit 5f79149↗.