Closed code423n4 closed 3 years ago
Note for self: I don't think this is an issue; I think cmichel
missed the fact that _tokenTotalSupply()
includes the reserve fee. So the reserve is accounted for, albeit obscurely. Might need a test for this one though.
This has been found to be a non-issue. See PR (https://github.com/pooltogether/pooltogether-pool-contracts/pull/317)
As @asselstine alluded to - the reserve balance is included within the result from _tokenTotalSupply()
so reserve is not captured twice.
Closing per sponsor's explanation
Handle
cmichel
Vulnerability details
The
PrizePool.captureAwardBalance
function takes fees repeatedly on the same interest. One would expectunaccountedPrizeBalance
to be0
in any repeated calls, but it's not.Assume the following example scenario with a 10% reserve fee:
captureAwardBalance
:totalInterest = 1000, _currentAwardBalance = 500 => unaccountedPrizeBalance = 1000 - 500 = 500.
With a 10% reserve fee on it,unaccountedPrizeBalance = 450 => _currentAwardBalance = 500 + 450
.captureAwardBalance
immediately again and it'll take a fee ontotalInterest = 1000, _currentAwardBalance = 500 => unaccountedPrizeBalance = 1000 - 950 = 50
. Adding another5
to the reserve.Impact
Instead of taking a reserve fee
f
(0 <= f < 1.0 = 100%
), a higher fee off / (1 - f)
can be taken by repeated calls tocaptureAwardBalance
. The pool creator (or reserve registry owner) can even use this with a high reserve fee (~99%) to rug the entire pool investments by inflating thereserveTotalSupply
to almost any arbitrary value and later redeeming everything usingwithdrawReserve
.Recommended Mitigation Steps
When determining the amount to take a fee on (
unaccountedPrizeBalance
), one needs to take into account not only the after-fee amount (_currentAwardBalance
) but the pre-fee amount. This probably requires tracking another variable.