Closed sherlock-admin closed 1 year ago
Duplicate of #73
I marked this as a duplicate of #73 because the underlying condition (a zero value turns off the slippage threshold in the TradingModule) is the fundamental problem in both reported issues. Although the initial affected code is in different locations, the core of the issue is in the same location.
Both issues were reported by the same auditor and I do feel that given their understanding of the issue they could have reported them both as a single issue rather than two.
xiaoming90
high
Existing Slippage Control Can Be Bypassed During Reinvest Rewards
Summary
The existing slippage control can be bypassed/disabled when reinvesting rewards, thus allowing the trade to be executed without consideration of its slippage.
Vulnerability Detail
The
maxRewardTradeSlippageLimitPercent
of the vault is set to 5% as per the environment file provided by Notional.https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/scripts/BalancerEnvironment.py#L45
When a user calls the
reinvestReward
function, the vault will validate that the slippage defined by the caller is within the acceptable slippage range within theTwoTokenAuraRewardUtils._executeRewardTrades
function at Line 126. It will pass thestrategyContext.vaultSettings.maxRewardTradeSlippageLimitPercent
predefined by Notional to theTwoTokenAuraRewardUtils._executeRewardTrades
function.https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/MetaStable2TokenAuraVault.sol#L164
https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/balancer/external/MetaStable2TokenAuraHelper.sol#L114
Within the
TwoTokenAuraRewardUtils._executeRewardTrades
function, it will validate that the user-defined slippage does not exceed the designated threshold (strategyContext.vaultSettings.maxRewardTradeSlippageLimitPercent = 5%) in Line 60-61. The transaction will revert if it exceeds the threshold. Note that theslippageLimit
is equal tomaxRewardTradeSlippageLimitPercent
which is5%
.There is an edge case with the condition at Line 60-61. Consider the following cases:
params.primaryTrade.tradeParams.oracleSlippagePercent
= 4% andslippageLimitPercent
= 5%, the condition will evaluate asFalse
and transaction will not revert.params.primaryTrade.tradeParams.oracleSlippagePercent
= 6% andslippageLimitPercent
= 5%, the condition will evaluate asTrue
and transaction will revert because it exceeds the designated threshold.params.primaryTrade.tradeParams.oracleSlippagePercent
= 0% andslippageLimitPercent
= 5%, the condition will evaluate asFalse
and transaction will not revert.The problem is that when
callbackData.oracleSlippagePercent
is0%
, this effectively means that there is no slippage limit. This essentially exceeded the designated threshold (strategyContext.vaultSettings.maxRewardTradeSlippageLimitPercent = 5%), and the transaction should revert instead, but it did not.https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/balancer/internal/reward/TwoTokenAuraRewardUtils.sol#L48
Within
executeTradeWithDynamicSlippage
function, it will calculate thetrade.limit
by calling thePROXY.getLimitAmount
. Thetrade.limit
is the maximum amount of sellToken that can be sold OR the minimum amount of buyToken the contract is expected to receive from the DEX depending on whether you are performing a sell or buy.https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/trading/TradingModule.sol#L109
Within the
TradingUtils._getLimitAmount
function, when theslippageLimit
is set to0
,limitAmount
will be set totype(uint256).max
. See Line 187limitAmount
will be set to0
. See Line 207These effectively remove the slippage limit. Therefore, a malicious user can specify the
callbackData.oracleSlippagePercent
to be0%
to bypass the slippage validation check.https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/trading/TradingUtils.sol#L162
Proof-of-Concept
The following test case shows that when the slippage is set to 6% (6e6), the transaction will be reverted and fails the test. This is working as intended because the slippage (6%) exceeded the threshold (
maxRewardTradeSlippageLimitPercent
=5%
).The following test case shows that when the slippage is set to 0, the transaction does not revert and passes the test. This is not working as intended because having no slippage (0) technically exceeded the threshold (
maxRewardTradeSlippageLimitPercent
=5%
).Impact
Malicious users can trigger the permissionless
reinvestReward
function and cause the trade to suffer huge slippage. This results in loss of assets for the vaults and their users.Code Snippet
https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/scripts/BalancerEnvironment.py#L45 https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/MetaStable2TokenAuraVault.sol#L164 https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/balancer/external/MetaStable2TokenAuraHelper.sol#L114 https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/vaults/balancer/internal/reward/TwoTokenAuraRewardUtils.sol#L48 https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/trading/TradingModule.sol#L109 https://github.com/sherlock-audit/2022-09-notional/blob/main/leveraged-vaults/contracts/trading/TradingUtils.sol#L162
Tool used
Manual Review
Recommendation
Update the
TwoTokenAuraRewardUtils._executeRewardTrades
andBoosted3TokenAuraRewardUtils._executeRewardTrades
functions to revert if the slippage is set to zero.Duplicate of #73