hyh - TOFT and mTOFT wrapping and executor swaps don't control for `msg.value` when deal with gas tokens, so any excess native token funds can be immediately stolen by anyone via back-running #112
TOFT and mTOFT wrapping and executor swaps don't control for msg.value when deal with gas tokens, so any excess native token funds can be immediately stolen by anyone via back-running
Summary
In the course of native token operations msg.value isn't controlled to match amount, so all the native tokens send over by users in excess of amount, i.e. msg.value - amount, can be stolen by a back-running attacker (who specify their amount < msg.value).
Vulnerability Detail
Residue native funds from TOFT, mTOFT and executor contract balances can be stolen.
Particularly, TOFT and mTOFT wrapping are directly user-facing, so the excess funds occurrences can be expected in general. There is also a probability of user mistakes, when msg.value - amount isn't a residue, but something material, being a result of user operational mistake. Attacker can setup a bot, automatically tracking all such events, immediately extracting these funds with back-running whenever the expected result exceeds gas costs.
Impact
The remainder funds from mentioned operations can be stolen by the next caller. The prerequisite is user supplying more than amount, so the probability is low (but not very low as the operations are user facing). Funds stealing impact has high severity.
Likelihood: Low + Impact: High = Severity: Medium.
Code Snippet
msg.value isn't controlled to match amount in many instances, e.g.:
function _swapAndTransferToSender(
bool sendBack,
address tokenIn,
address tokenOut,
>> uint256 amountIn,
bytes memory data
) internal returns (uint256 amountOut) {
SLeverageSwapData memory swapData = abi.decode(data, (SLeverageSwapData));
// If the tokenIn is a tOFT, unwrap it. Handles ETH and ERC20.
if (swapData.toftInfo.isTokenInToft) {
>> tokenIn = _handleToftUnwrap(tokenIn, amountIn);
}
// Approve the swapper to spend the tokenIn, and perform the swap.
>> tokenIn.safeApprove(address(swapper), amountIn);
IZeroXSwapper.SZeroXSwapData memory swapperData =
abi.decode(swapData.swapperData, (IZeroXSwapper.SZeroXSwapData));
amountOut = swapper.swap(swapperData, amountIn, swapData.minAmountOut);
function _handleToftUnwrap(address tokenIn, uint256 amountIn) internal returns (address tokenToSwap) {
ITOFT(tokenIn).unwrap(address(this), amountIn); // Sends ETH to `receive()` if not an ERC20.
tokenIn = ITOFT(tokenIn).erc20();
// If the tokenIn is ETH, wrap it to WETH.
if (tokenIn == address(0)) {
>> weth.deposit{value: amountIn}();
tokenToSwap = address(weth);
} else {
tokenToSwap = tokenIn;
}
}
Tool used
Manual Review
Recommendation
Consider adding the amount checks for the native case, e.g.:
hyh
medium
TOFT and mTOFT wrapping and executor swaps don't control for
msg.value
when deal with gas tokens, so any excess native token funds can be immediately stolen by anyone via back-runningSummary
In the course of native token operations
msg.value
isn't controlled to matchamount
, so all the native tokens send over by users in excess ofamount
, i.e.msg.value - amount
, can be stolen by a back-running attacker (who specify theiramount < msg.value
).Vulnerability Detail
Residue native funds from TOFT, mTOFT and executor contract balances can be stolen.
Particularly, TOFT and mTOFT wrapping are directly user-facing, so the excess funds occurrences can be expected in general. There is also a probability of user mistakes, when
msg.value - amount
isn't a residue, but something material, being a result of user operational mistake. Attacker can setup a bot, automatically tracking all such events, immediately extracting these funds with back-running whenever the expected result exceeds gas costs.Impact
The remainder funds from mentioned operations can be stolen by the next caller. The prerequisite is user supplying more than
amount
, so the probability is low (but not very low as the operations are user facing). Funds stealing impact has high severity.Likelihood: Low + Impact: High = Severity: Medium.
Code Snippet
msg.value
isn't controlled to matchamount
in many instances, e.g.:https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/mTOFT.sol#L287-L303
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/TOFT.sol#L242-L251
No amount information is left at this point:
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/BaseTOFT.sol#L78-L81
Similarly, executor's
getCollateral()
andgetAsset()
don't ensure thatmsg.value
is being utilized fully, e.g.:As
assetAmountIn = amountIn
is used instead:https://github.com/sherlock-audit/2024-02-tapioca/blob/main/Tapioca-bar/contracts/markets/leverage/BaseLeverageExecutor.sol#L129-L147
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/Tapioca-bar/contracts/markets/leverage/BaseLeverageExecutor.sol#L169-L179
Tool used
Manual Review
Recommendation
Consider adding the amount checks for the native case, e.g.:
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/mTOFT.sol#L287-L303
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/mTOFT.sol#L93-L98
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/TOFT.sol#L242-L251
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/tOFT/TOFT.sol#L47-L50
Duplicate of #132