Open code423n4 opened 1 year ago
Would have liked a coded POC to show a scenario in which the execution does happen
I agree that adding this require
or assert
check could prevent a potential attack scenario.
The Drips contracts are built on the assumption that the value will never be negative.
It would be some kind of sanity-check
for an unknown edge case an attacker could exploit.
I think we should consider adding this check as a sanity check.
However, it is hard to judge the severity because it is built on the assumption of an unkown edge case which might also not exist.
[disagree with severity: QA] If a bug described here actually occurs, sanity checks probably won't help, it means that the state is corrupted and the protocol is completely broken. I'm not sure if checks like this are worth the gas, there are many places where things must fit perfectly and many edge cases that can't be covered with simple assertions. I'm not sure if the mitigation is worth adding.
CodeSandwich marked the issue as disagree with severity
@berndartmueller can you come up with an instance of the invariant being broken?
@GalloDaSballo Unfortunately, no. I submitted this finding to raise awareness that if there’s a yet unknown way to break the invariant, it’s catastrophic. But it can be prevented by adding appropriate assertions
@GalloDaSballo Unfortunately, no. I submitted this finding to raise awareness that if there’s a yet unknown way to break the invariant, it’s catastrophic. But it can be prevented by adding appropriate assertions
Got it, pls lmk if you can find one
I think in lack of an instance the most appropriate severity is QA Low
L
GalloDaSballo changed the severity to QA (Quality Assurance)
GalloDaSballo marked the issue as grade-a
Lines of code
https://github.com/code-423n4/2023-01-drips/blob/9fd776b50f4be23ca038b1d0426e63a69c7a511d/src/Drips.sol#L289
Vulnerability details
Yet unreceived drips can be collected by a user via the
DripsHub.receiveDrips
function. It is only ever possible to receive drips beginning with the drip starting atnextReceivableCycle
until eithermaxCycles
or the last cycle.The amount to be dripped is calculated by iterating over all those cycles and summing up the delta amounts (represented by the
int128
values inamtDeltas[cycle].thisCycle
andamtDeltas[cycle].nextCycle
). This accumulator is assumed to always be positive. Otherwise, it would mean that a user loses token amounts instead of having tokens dripped to them.However, in the (unlikely) event that a negative
amtDeltas[cycle].thisCycle
value is present in the cycles to be received, the cast fromint128
touint128
in L289 will result in a huge positive value which is returned to theDripsHub.receiveDrips
function. This can be used to drain all funds from theDripsHub
contract.Impact
All funds from the
DripsHub
for a given asset can be drained.Proof of Concept
DripsHub.sol#L244
Drips.sol#L242
Drips.sol#L289
Consider the following attack scenario:
state.amtDeltas[cycle].thisCycle
value within the cycles to be receivedDripsHub.receiveDrips
with an attacker controllableuserId
,erc20
asset to drain funds from and amaxCycles
value which is appropriately large to iterate only over the cycle(s), which leads to the negativeamtPerCycle
valueamtPerCycle
value is unsafely cast touint128
in L289 andreceivedAmount
is set to a huge positive valuereceivedAmt
value is returned to theDripsHub.receiveDrips
function and is added to the splittable balance of the attacker-controlleduserId
userId
to split the huge and likely too large token amount value into smaller chunks which can be collected and ultimately withdrawn (capped by the amount of escrowed ERC-20 tokens in theDripsHub
contract)Multiple factors contribute to the chosen severity of this issue:
DripsHub
contractthisCycle
valueTools Used
Manual review
Recommended mitigation steps
Consider adding additional security mechanisms to safeguard against overflows.
For example, add an assertion in
Drips._receiveDripsResult
to check thatamtPerCycle
is not negative: