Platform: Code4rena
Start Date: 30/05/2023
Pot Size: $300,500 USDC
Total HM: 79
Participants: 101
Period: about 1 month
Judge: Trust
Total Solo HM: 36
Id: 242
League: ETH
Rank: 26/101
Findings: 4
Award: $2,070.23
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: shealtielanz
Also found by: 0xStalin, 0xnev, Breeje, RED-LOTUS-REACH, kutugu, xuwinnie
333.3031 USDC - $333.30
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/strategies/TalosStrategySimple.sol#L42 https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/libraries/PoolActions.sol#L50
Loss of funds due to MEV Sandwich attacks.
Rebalancing is done using doRebalance
method in TalosStrategySimple
.
File: TalosStrategySimple.sol function doRebalance() internal override returns (uint256 amount0, uint256 amount1) { int24 baseThreshold = tickSpacing * optimizer.tickRangeMultiplier(); PoolActions.ActionParams memory actionParams = PoolActions.ActionParams(pool, optimizer, token0, token1, tickSpacing); @-> PoolActions.swapToEqualAmounts(actionParams, baseThreshold); (tickLower, tickUpper, amount0, amount1, tokenId, liquidity) = nonfungiblePositionManager.rerange(actionParams, poolFee); }
Line in focus here is:
PoolActions.swapToEqualAmounts(actionParams, baseThreshold);
swapToEqualAmounts
method in PoolActions.sol
library:
File: PoolActions.sol function swapToEqualAmounts(ActionParams memory actionParams, int24 baseThreshold) internal { @-> (bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) = actionParams .pool .getSwapToEqualAmountsParams( actionParams.optimizer, actionParams.tickSpacing, baseThreshold, actionParams.token0, actionParams.token1 ); //Swap imbalanced token as long as we haven't used the entire amountSpecified and haven't reached the price limit actionParams.pool.swap( address(this), zeroForOne, amountSpecified, @-> sqrtPriceLimitX96, abi.encode(SwapCallbackData({zeroForOne: zeroForOne})) ); }
Here, the value of sqrtPriceLimitX96
is calculated through getSwapToEqualAmountsParams
method in PoolVariables.sol
library. It basically represents the square root of the lowest or highest price that you are willing to perform the trade at. So basically it is a slippage control parameter.
File: PoolVariables.sol 232: (uint160 sqrtPriceX96, int24 currentTick,,,,,) = _pool.slot0(); 251: uint160 exactSqrtPriceImpact = (sqrtPriceX96 * (_strategy.priceImpactPercentage() / 2)) / GLOBAL_DIVISIONER; 252: sqrtPriceLimitX96 = zeroForOne ? sqrtPriceX96 - exactSqrtPriceImpact : sqrtPriceX96 + exactSqrtPriceImpact;
getSwapToEqualAmountsParams
method uses the _pool.slot0
to determine The current price of the pool as a sqrt. slot0 is the most recent data point and can easily be manipulated using Flash Loans.
Because of this, the value of sqrtPriceLimitX96
can be manipulated and that value will be passed in swap
method.
With this knowledge, a malicious MEV bot could watch for these transactions in the mempool. When it sees such a transaction, it could perform a "sandwich attack", trading massively in the same direction as the trade in advance of it to push the price out of whack, and then trading back after us, so that they end up pocketing a profit at our expense.
VS Code
Use TWAP
rather than slot0
.
MEV
#0 - c4-judge
2023-07-11T14:04:06Z
trust1995 marked the issue as duplicate of #823
#1 - c4-judge
2023-07-11T14:04:11Z
trust1995 marked the issue as satisfactory
🌟 Selected for report: Breeje
1712.1701 USDC - $1,712.17
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyStaked.sol#L28
There are many instance of this, but to understand things better, taking the example of createTalosV3Strategy
method.
The createTalosV3Strategy
function deploys a new TalosStrategyStaked
contract using the create, where the address derivation depends only on the arguments passed.
At the same time, some of the chains like Arbitrum and Polygon are suspicious of the reorg attack.
File: TalosStrategyStaked.sol function createTalosV3Strategy( IUniswapV3Pool pool, ITalosOptimizer optimizer, BoostAggregator boostAggregator, address strategyManager, FlywheelCoreInstant flywheel, address owner ) public returns (TalosBaseStrategy) { return new TalosStrategyStaked( // @audit-issue Reorg Attack pool, optimizer, boostAggregator, strategyManager, flywheel, owner ); }
Even more, the reorg can be couple of minutes long. So, it is quite enough to create the TalosStrategyStaked
and transfer funds to that address using deposit
method, especially when someone uses a script, and not doing it by hand.
Optimistic rollups (Optimism/Arbitrum) are also suspect to reorgs since if someone finds a fraud the blocks will be reverted, even though the user receives a confirmation.
Same issue can affect factory contracts in Ulysses omnichain contracts as well with more severe consequences.
Can refer this an issue previously report here to have more understanding about it.
Exploits involving Stealing of funds.
VS Code
Deploy such contracts via create2
with salt
.
Other
#0 - c4-judge
2023-07-11T14:43:36Z
trust1995 marked the issue as primary issue
#1 - c4-judge
2023-07-11T14:43:40Z
trust1995 marked the issue as satisfactory
#2 - c4-sponsor
2023-07-12T18:39:57Z
0xLightt marked the issue as sponsor disputed
#3 - c4-sponsor
2023-07-18T12:02:27Z
0xLightt marked the issue as sponsor confirmed
#4 - c4-judge
2023-07-25T11:19:52Z
trust1995 marked the issue as selected for report
#5 - T1MOH593
2023-07-26T10:19:44Z
In my opinion Low severity is more appropriate as there is no loss of funds when reorg attack happens
So, it is quite enough to create the TalosStrategyStaked and transfer funds to that address using deposit method, especially >when someone uses a script, and not doing it by hand.
But in described scenario there is no loss of funds of users, as they deposit to TalosStrategyStaked and receive shares in exchange. So they don't lose funds because anytime they can exchange shares back. Report lacks severe impact and is more of informational type.
#6 - trust1995
2023-07-26T12:33:39Z
@Breeje16 , would you like to clarify how attacker is able to materialize damage using the re-org attack using their controlled strategy?
#7 - 0xLightt
2023-09-06T17:22:35Z
🌟 Selected for report: Madalad
Also found by: 0xCiphky, 0xSmartContract, 8olidity, BPZ, Breeje, BugBusters, Kaiziron, MohammedRizwan, Oxsadeeq, Qeew, RED-LOTUS-REACH, T1MOH, brgltd, chaduke, giovannidisiena, lsaudit, peanuts, tsvetanovv
10.4044 USDC - $10.40
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L144-L145
Loss of funds due to Allowing Maximum Slippage.
There are many instances throughout the codebase where slippage protection has not been used and 0
has been passed to slippage control instead.
Eg: deposit
method in TalosBaseStrategy
:
File: TalosBaseStrategy.sol (liquidityDifference, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: _tokenId, amount0Desired: amount0Desired, amount1Desired: amount1Desired, @-> amount0Min: 0, // @audit-issue No Slippage Protection @-> amount1Min: 0, deadline: block.timestamp // @audit-issue Miner controlled Deadline }) );
In Uniswap's example docs here, it is clearly stated that:
We cannot change the boundaries of a given liquidity position using the Uniswap v3 protocol; increaseLiquidity can only increase the liquidity of a position.
In production, amount0Min and amount1Min should be adjusted to create slippage protections.
It is worth noting the second point that amount0Min
and amount1Min
acts as a slippage protection and it should be adjusted in the production in order to protect the users from High Slippage.
But in the following instance, this slippage parameter is set to zero which is exposing the transaction to maximum slippage:
VS Code
Allow users to pass the value of amountXMin
as parameter.
Uniswap
#0 - c4-judge
2023-07-09T17:36:52Z
trust1995 marked the issue as primary issue
#1 - c4-judge
2023-07-11T14:11:30Z
trust1995 marked the issue as satisfactory
#2 - c4-judge
2023-07-11T17:03:35Z
trust1995 marked the issue as duplicate of #177
#3 - c4-judge
2023-07-11T17:04:10Z
trust1995 changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-07-11T17:04:19Z
trust1995 changed the severity to 3 (High Risk)
#5 - c4-judge
2023-07-25T08:54:03Z
trust1995 changed the severity to 2 (Med Risk)
🌟 Selected for report: 0xnev
Also found by: 0xSmartContract, Breeje, BugBusters, ByteBandits, IllIllI, Kaiziron, Madalad, MohammedRizwan, SpicyMeatball, T1MOH, kutugu, nadin, peanuts, said, shealtielanz, tsvetanovv
14.356 USDC - $14.36
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L73
AMMs provide their users with an option to limit the execution of their pending actions, such as swaps or adding and removing liquidity. The most common solution is to include a deadline timestamp as a parameter (for example: Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades.
Currently there are 3 external methods in UlyssesRouter
:
addLiquidity
removeLiquidity
swap
2 things are most important while interacting with AMMs:
minOutput
as a function parameter.A MEV bot can detect the pending transactions. Since the outdated maximum slippage value now allows for high slippage, the bot sandwiches the transaction, resulting in significant profit for the bot and significant loss for user.
This second part is not handled in UlyssesRouter
.
Potential loss of funds
VS Code
Add a deadline check exactly how uniswap does in functions related to adding liquidity, removing liquidity and swapping.
MEV
#0 - c4-judge
2023-07-09T16:12:20Z
trust1995 marked the issue as duplicate of #171
#1 - c4-judge
2023-07-09T16:18:20Z
trust1995 marked the issue as satisfactory
🌟 Selected for report: 0xnev
Also found by: 0xSmartContract, Breeje, BugBusters, ByteBandits, IllIllI, Kaiziron, Madalad, MohammedRizwan, SpicyMeatball, T1MOH, kutugu, nadin, peanuts, said, shealtielanz, tsvetanovv
14.356 USDC - $14.36
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L208
Miner can potentially hold the transaction which results in loss of funds for users.
File: TalosBaseStrategy.sol (liquidityDifference, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: _tokenId, amount0Desired: amount0Desired, amount1Desired: amount1Desired, amount0Min: 0, amount1Min: 0, @-> deadline: block.timestamp }) );
AMMs provide their users with an option to limit the execution of their pending actions, such as swaps or adding and removing liquidity. The most common solution is to include a deadline timestamp as a parameter (for example in Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades as interaction with the pools is very time sensitive, if transaction gets hold for sometime than it can lead to bad trades unknowingly by the Users and can potentially result in loss of funds.
All the functions in the codebase that interact with uniswap pools have a deadline parameter passed as block.timestamp
, which means that whenever the miner decides to include the transaction in a block, it will be valid at that time, since block.timestamp
will be the current timestamp at the time of mining. but here, there is a possibility of a malicious miner to hold the transaction how whatever amount he/she wants.
This is tbe reason, instead of block.timestamp
, an extra arguments should be passed with the functions and user should be allowed to pass a valid deadline for it so that in case a malicious miner tries to hold it, then the transaction reverts as per the deadline specified by the user. Having slippage is an issue for any AMMs but having outdated Slippage protection can also be a dangerous attack vector.
Link to a similar issue reported in Paraspace Audit by Code4rena.
VS Code
Add deadline arguments in all functions that interact with uniswap pools, and pass it along to these calls.
Uniswap
#0 - c4-judge
2023-07-09T11:17:07Z
trust1995 marked the issue as duplicate of #171
#1 - c4-judge
2023-07-09T11:17:12Z
trust1995 marked the issue as satisfactory