Open code423n4 opened 1 year ago
The issue is well demonstrated, properly formatted, contains a coded POC. Marking as HQ.
0xSorryNotSorry marked the issue as high quality report
0xSorryNotSorry marked the issue as primary issue
Testing against a chainforked mainnet, I cannot replicate this PoC.
ElliotFriedman marked the issue as sponsor disputed
forge test --fork-url MAINNET_RPC_URL --fork-block-number 17413937 -vvvv
This report exposes a few flaws in the code, but given that this is live with Compound, and Compound hasn't been hacked to their whole TVL, or even DoS'd, it can't in all seriousness be considered a High.
The two flaws are:
borrowRateMantissa > borrowRateMaxMantissa
instead of capping borrowRateMantissa
to borrowRateMaxMantissa
.borrowRateMantissa <= borrowRateMaxMantissa
is a clear invariant that should be checked, and operations that violate it should be reverted.However, I understand if the sponsor doesn't want to modify Compound's code, since it is working out for Compound so far.
@ElliotFriedman, I would suggest that you investigate this vulnerability until you fully understand it. @kankodu, would you know by chance how is it that Compound v2 defends itself against this vulnerability?
In compound v2 they use borrow caps to avoid this attack vector.
there are borrow caps in the moonwell codebase
you can't make a PoC against another codebase we've forked off of and use that to demonstrate the vulnerability as the diff is pretty significant at this point and there are separate behaviors. Feel free to view the live deployed contracts on base here, and verify the vulnerability by doing a chainfork test on base with this instance of the lending market. https://github.com/moonwell-fi/moonwell-contracts-v2/blob/main/test/proposals/Addresses.sol
I understand the nature of the attack, however there are a few things that prevent it from happening.
We need to be realistic, an attack vector that takes 100 days to be executed is vulnerable to governance action, so the likelihood of the attack succeeding is low.
In addition, the sponsor is aware of the existence of borrow caps and while they might not be aware of how they should be set to avoid this particular attack, they might still set them to reasonable values that prevent it the same.
The report is being downgraded to Medium, in line with the assessment of the other wardens that reported that accrueInterest
can be caused to revert and DoS the protocol.
alcueca changed the severity to 2 (Med Risk)
alcueca marked issue #70 as primary and marked this issue as a duplicate of 70
alcueca marked the issue as satisfactory
alcueca marked the issue as duplicate of #40
alcueca marked the issue as not a duplicate
@C4-Staff This finding should not have been included in the report:
selected for report
until August 28th, and the final review of issues took place on August 22nd-23rd.In addition, the sponsor is aware of the existence of borrow caps and while they might not be aware of how they should be set to avoid this particular attack, they might still set them to reasonable values that prevent it the same.
In fact, every active market on the Moonwell protocol has both a borrow cap set and multiple active risk managers adjusting them regularly. The reason @kankodu didn't provide a working PoC against live Moonwell contracts is because they know it isn't possible. I'm sure they tried.
@lyoungblood
First, let's accept that the PoC is not valid, it is just a guideline to understand the theoretical attack. Submissions without PoC are accepted if the code is clearly wrong, and in this case it is clear that a vulnerability was brought over from Compound v2.
Second, if the warden would have coded a PoC working on live contracts, the submission would have been considered as High, most likely. It is not being rated as High.
On one hand: The code is wrong, and it can lead to losses if governance messes up. As such, this would be rated Medium.
On the other hand: The attack is VERY unlikely to be initiated because Moonwell boasts of borrow caps right there in the README, and after initiated VERY unlikely to succeed because of the long time needed to execute it. I could be persuaded to think of this as QA, as there are many other cases where governance errors lead to losses that are only QA. However, those tend to be because of design limitations, and not easily corrected in code.
I do not think that this would reflect wrongly on C4, judged either way. It really is borderline.
The fact remains that a vulnerability was ported from Compound v2 that Moonwell should have fixed. Teams forking Compound v2 in the future would benefit from the Moonwell report by not repeating this mistake.
On the other hand: The attack is VERY unlikely to be initiated because Moonwell boasts of borrow caps right there in the README, and after initiated VERY unlikely to succeed because of the long time needed to execute it. I could be persuaded to think of this as QA, as there are many other cases where governance errors lead to losses that are only QA. However, those tend to be because of design limitations, and not easily corrected in code.
I think classifying this as QA is likely the best outcome. The Compound team put a lot of thought into the design, and intentionally chose to not solve for unlikely scenarios like this that can't be exploited in practice. Choosing to go back and fix vulnerabilities that are theoretical in nature (can only be executed if borrow caps are not set and governance ignores obvious market manipulation for 100+ days) could introduce other vulnerabilities and make the protocol less secure, not more.
We are balancing the desire to use the same codebase that has been battle-hardened on live networks for 3+ years vs. the desire to go back and fix bugs. Unfortunately this bug does not rise to the level of being worth a fix, since it is trivially mitigated. QA seems like a good category for it.
alcueca changed the severity to QA (Quality Assurance)
alcueca marked the issue as not selected for report
alcueca marked the issue as grade-a
Agree with the sponsor, downgraded to QA.
Lines of code
https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/MToken.sol#L403 https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/IRModels/JumpRateModel.sol#L84-L94 https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/IRModels/WhitePaperInterestRateModel.sol#L51-L58
Vulnerability details
Impact
The bug in Compound V2
_addReserves
method that is open to everyone.borrowRateMantissa
goes nearborrowRateMaxMantissa
but does not go over it.accrueInterest
on the cToken to lock this almost absurdly high borrow rate. (0.0005% per block).borrowRateMantissa
goes overborrowRateMaxMantissa
and this means that cToken is now in lockdown. No operations can be done as all the functions callaccrueInterest
internally which fails withborrow rate is absurdly high
error.cash
to the cToken to bringborrowRateMantissa
to be less thanborrowRateMaxMantissa
. At this point, their cTokens will have increased so much more in value that they can borrow other genuine tokens from the protocol. The longer the attacker waits, the higher the profit.What does it mean for MoonWell
Proof of Concept
I am adding POC for compound v2 on mainnet.
For Compound v2 I am using PAX dollar as an example. Right now, collateral factor is set to 0 for that so I am artificially setting it to 80% as this is required for the attack to work.
Initiate a new foundry repo by running forge init. see here
Add the below code in
test/DonationAttack.t.sol
Run the test with
forge test --fork-url MAINNET_RPC_URL -vvvv
Tools Used
Recommended Mitigation Steps
borrowRateMantissa
hitsborrowRateMaxMantissa
, handle it by usingborrowRateMaxMantissa
for the current borrow rate.Also, Be aware that for different chain that have different block time,borrowRateMaxMantissa
would translate to different max borrow rates.Assessed type
Other