Platform: Code4rena
Start Date: 08/03/2023
Pot Size: $60,500 USDC
Total HM: 2
Participants: 123
Period: 7 days
Judge: hansfriese
Id: 220
League: ETH
Rank: 48/123
Findings: 1
Award: $154.74
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rokso
Also found by: 0x52, 0xnev, ABA, BPZ, DadeKuma, Haipls, J4de, Jeiwan, Josiah, Krace, LegendFenGuin, Lirios, MadWookie, RaymondFam, Ruhum, Toshii, UdarTeam, aashar, ak1, anodaram, auditor0517, carlitox477, cccz, jekapi, juancito, kaden, kenzo, minhquanym, nobody2018, rbserver, rokso, ulqiorra
154.74 USDC - $154.74
This example shows us one tricky in math floor function.
1.51 + 1.51 - 1.00 - 1.00 - 1.00 > 0 f(1.51) + f(1.51) - f(1.00) - f(1.00) - f(1.00) < 0 // f is math floor function
To make it easy to describe, let's say that timelockMultiplier
is 100
.
points
calculation part of stake/withdraw LP are on Line 1155 and Line 1623.
uint256 points = amount * 100 / 1e18 * timelockMultiplier / _DIVISOR;
So,
amount = 1.51 * 10**16
=> points = 1
amount = 1.00 * 10**16
=> points = 1
Step | Activity | lpPosition.amount | lpPosition.points |
---|---|---|---|
(orginal) | 0 | 0 | |
Step 1 | Stake 1.51 * 10**16 LP token | 1.51 * 10**16 | 1 |
Step 2 | Stake 1.51 * 10**16 LP token | 3.02 * 10**16 | 2 |
Step 3 | Withdraw 1.00 * 10**16 LP token | 2.02 * 10**16 | 1 |
Step 4 | Withdraw 1.00 * 10**16 LP token | 1.02 * 10**16 | 0 |
Step 5 | Withdraw 1.00 * 10**16 LP token | 0.02 * 10**16 | max(uint256) - 1 |
After Step 5, the points should be -1. Since the datatype is unsinged integer, it will be INFINITY.
This can cause several critical attacks.
In line 1388-1392, which is calculating user share amount in function getPoolReward
uint256 share = points * _PRECISION / pool.totalPoints * totalReward; uint256 daoShare = share * pool.daoTax / (100 * _DIVISOR); share /= _PRECISION; daoShare /= _PRECISION; return ((share - daoShare), daoShare);
(We can assume that pool.totalPoints
is not negative because we have other users' sharing)
In case of points = max(uint256) - 1
, share
can be unexpected value.
The malicious user can getReward huge amount of asset.
https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1155 https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1623-L1627 https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1388-L1392
(Nothing, just looking code on VSCode)
We can recalculate lpPosition.points
based on newAmount
in _withdrawLP
.
uint256 points = amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; // Update the caller's LP token stake. lpPosition.amount -= amount; lpPosition.points -= points; // Update the pool point weights for rewards. pool.totalPoints -= points;
uint256 points = amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; // Update the caller's LP token stake. lpPosition.amount -= amount; uint256 newPoints = lpPosition.amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; pool.totalPoints -= lpPosition.points - newPoints; lpPosition.points = newPoints;
Also, need to fix in the same way in _stakeLP
.
#0 - c4-judge
2023-03-16T06:38:21Z
hansfriese marked the issue as satisfactory
#1 - c4-judge
2023-03-16T06:38:33Z
hansfriese marked the issue as duplicate of #450
#2 - c4-judge
2023-03-17T01:09:04Z
hansfriese marked the issue as duplicate of #261