Open c4-bot-10 opened 4 months ago
Known and intended,Invalid.
Hi @alex-ppg, the above comment on this issue from the validator is confusing, can't seem to find where this bug case was classified as known during the audit timeline, kindly reassess the finding.
Hey @Bauchibred, thank you for the PJQA contribution. I will preface all validation repository finding responses by stating that they are not evaluated by judges directly and are only evaluated by the validators if they are deemed unsatisfactory.
This appears to be a duplicate of #15, and the same rationale applies here for the submission being downgraded to QA (L).
This paragraph is included in all of my responses and confirms that no further feedback is expected in this submission as PJQA has concluded. You are free to refute any of my statements factually, however, I strongly implore you to do this with actual code references and examples.
Lines of code
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L204-L229
Vulnerability details
Proof of Concept
From the readMe, we can see the normal flow in the case one want's to contribute, https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/README.md#L185, i.e:
whitelist
contract, there'scheckWhitelist
function which checks if:from
address is uniswap v3 pool which holds liquidity, then it means, this transfer is thebuy
action.Going through the code snippets for the on-chain logic attached to this flow, we can see the
checkWhitelist
function here https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L204-L229This function is called via the
_beforeTokenTransfer
hook as clearly documented here and it makes a call to theIOracle(_oracle).peek()
whose implementation is here: https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/oracles/uniswap/UniswapV3Oracle.sol#L39-L46From the
UniswapV3Oracle
contract we can see the value ofPERIOD
that gets checked against thelongestPeriod
, and thePERIOD
value is 30 minutes, as clearly initialised here: https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/oracles/uniswap/UniswapV3Oracle.sol#L16Now the
peek
function is used to compute the twap of a Uniswap V3 pool using data from its oracle, and then it returns the final calculated(quotedWETHAmount * baseAmount * 95) / 1e20
, issue as tagged by the @audit tag here is that in the case wherePERIOD < longestPeriod
protocol automatically drops the 30 minutesPERIOD
and uses thelongestPeriod
duration that could be very short, keep in mind that as already hinted in this report above and the readMe, at the early stages of protocol we should expect the intialization of the VULT/ETH pool on uniswap, which would mean that the observation could even be as short as 1 block (12 secs), since an attacker can just backrun the intialization with their contribution.So since the oldest observation for a pool can be a very short timeframe especially when the pool is relatively new, the pool is heavily manipulatable, coupling this with the fact that protocol does not set a low limit as to how much short the observation period can be after querying the oldest observation from the uniswap pool, this then leads to the protocol to allow for ingestions of wrong prices, so a contributor can just back run the initialization by contributing when the pool is new by manipulating the VULT price calculated as
(quotedWETHAmount * baseAmount * 95) / 1e20;
and inflate their contribution.Impact
Since the contributed amount is directly gotten from the return data of peek while checking the whitelist
_contributed[to] += estimatedETHAmount;
in the case where the contribution is via a uniswap by, then theuint32 period = PERIOD < longestPeriod ? PERIOD : longestPeriod;
check allows for heavily manipulated data to be ingested, cause in our caselongestPeriod
here can be as low as a few blocks, which essentially leads to early tech savvy contributors to inflate the amount of their contributions having the upper hand over other users.Recommended Mitigation Steps
Consider scrapping the
PERIOD < longestPeriod ? PERIOD : longestPeriod;
check, or consider having a low boundary, i.e if thelongestPeriod
<= 5 minutes, revert the contribution and have the contributors wait a bit to ensure the pool is not as easily manipulatable, i.e apply these _(pseudo) _changes:Assessed type
Context