Platform: Code4rena
Start Date: 17/02/2022
Pot Size: $75,000 USDC
Total HM: 20
Participants: 39
Period: 7 days
Judges: moose-code, JasoonS
Total Solo HM: 13
Id: 89
League: ETH
Rank: 2/39
Findings: 4
Award: $7,480.73
π Selected for report: 2
π Solo Findings: 1
π Selected for report: danb
Also found by: Meta0xNull, Ruhum, cmichel, csanuragjain, hyh, kirk-baird, leastwood, minhquanym, robee, throttle
The VUSD.processWithdrawals
function only performs maxWithdrawalProcesses
(actually maxWithdrawalProcesses + 1
) iterations per call.
Withdrawals can be freely spammed by a griefer calling burn(amount)
with a zero amount.
All future withdrawals are blocked until someone calls processWithdrawals
to iterate through all the spam withdrawals.
Add a minimum withdrawal amount in withdraw
to increase the cost of the attack and make it infeasible.
#0 - atvanguard
2022-02-24T08:05:13Z
Duplicate of #119
The InsuranceFund.deposit
function mints initial shares
equal to the deposited amount.
The deposit / withdraw functions also use the VUSD contract balance for the shares computation. (balance() = vusd.balanceOf(address(this))
)
It's possible to increase the share price to very high amounts and price out smaller depositors.
deposit(_amount = 1)
: Deposit the smallest unit of VUSD as the first depositor. Mint 1 share and set the total supply and VUSD balance to 1
.1000.0
VUSD to the InsuranceFund
. The balance()
is now 1000e6 + 1
1000.0
VUSD will mint zero shares: shares = _amount * _totalSupply / _pool = 1000e6 * 1 / (1000e6 + 1) = 0
.withdraw(1)
to burn their single share and receive the entire pool balance, making a profit. (balance() * _shares / totalSupply() = balance()
)I give this a high severity as the same concept can be used to always steal the initial insurance fund deposit by frontrunning it and doing the above-mentioned steps, just sending the frontrunned deposit amount to the contract instead of the fixed 1000.0
.
They can then even repeat the steps to always frontrun and steal any deposits.
The way UniswapV2 prevents this is by requiring a minimum deposit amount and sending 1000
initial shares to the zero address to make this attack more expensive.
The same mitigation can be done here.
#0 - atvanguard
2022-02-24T05:26:48Z
Duplicate of #116
The ClearingHouse.updatePositions
function iterates over all amms
.
There is no limit to the number of AMMs that can be added in ClearingHouse.whitelistAmm
.
The transactions can fail if the arrays get too big and the transaction would consume more gas than the block limit. This will then result in a denial of service for the desired functionality and break core functionality. It also leads to a higher gas cost.
A similar issue was recently marked as medium severity.
It would be best to set a sanity maximum number of AMMs/ that can be added.
#0 - atvanguard
2022-02-24T08:06:27Z
Duplicate of #97
π Selected for report: cmichel
2785.5118 USDC - $2,785.51
The Oracle.getUnderlyingPrice
function divides the chainlink price by 100
.
It probably assumes that the answer for the underlying is in 8 decimals but then wants to reduce it for 6 decimals to match USDC.
However, arbitrary underlying
tokens are used and the chainlink oracles can have different decimals.
While most USD price feeds use 8 decimals, it's better to take the on-chain reported decimals into account by doing AggregatorV3Interface(chainLinkAggregatorMap[underlying]).decimals()
, see Chainlink docs.
The price should then be scaled down to 6 decimals.
#0 - atvanguard
2022-02-24T05:36:19Z
All chainlink USD pairings are expected to have 8 decimals hence disagreeing with severity; but yes agree that asserting this check when adding a new asset is a good idea.
#1 - moose-code
2022-03-06T14:51:49Z
Downgrading to medium. Dividing by magic numbers (100) should clearly comment assumptions