Open c4-bot-9 opened 10 months ago
0xSorryNotSorry marked the issue as sufficient quality report
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
The root cause seems to be the same with https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/877 but duping differently as the issue slightly differs.
Thanks for the quality of the report. Confirming this but disagree with the severity, given the size of the impact: the split of interests going to GUILD holders is expected to be small (it is set to 1% in the deployment file). So for a loan that pays 4% APR, GUILD token holders will receive 0.04% of the principal in interests after 1 year. For a single loan, this amount is small and probably not worth the gas/time cost of coding/running a frontrunner, taking flashloans, etc.
eswak (sponsor) acknowledged
eswak marked the issue as disagree with severity
Trumpero changed the severity to 2 (Med Risk)
Trumpero marked the issue as satisfactory
Trumpero marked the issue as selected for report
Hey @Trumpero, could you please take a second review at this?
How an exploiter can take a $10 million USDC flashloan and then wait for 150 days. Aren't flashloans meant to be repaid in the same transaction? Also, in repaying Alice's position, the repay() function uses msg.sender.
Note for warden: I'm interested in taking some of them flashloans myself. Any assistance on that would be appreciated.
Hey @0xbtk, I strongly advise you to read the report again, the Еxploiter never owns the flash loan for 150 days.
Also, in repaying Alice's position, the repay() function uses msg.sender.
About that, yes there was a mistake in the steps, Alice will repay it, lowering all other rewards.
This is a simpler sandwich attack.
Hey @0xbtk, I strongly advise you to read the report again, the Еxploiter never owns the flash loan for 150 days.
Also, in repaying Alice's position, the repay() function uses msg.sender.
About that, yes there was a mistake in the steps, Alice will repay it, lowering all other rewards.
This is a simpler sandwich attack.
Hey @Slavchew, I did review your PoC carefully, in your PoC you said that the attacker will take 10m flashloan here:
gUSDC.mint(EXPLOITER, 10_000_000e18);
Following that, you used warp
to warp 150 days before repaying the flashloan here:
vm.warp(block.timestamp + 150 days);
uint256 interest = _computeAliceLoanInterest(borrowTime, 100e18);
Could you please explain how can the exploiter do that in a real world scenario?
Hey @0xbtk, I strongly advise you to read the report again, the Еxploiter never owns the flash loan for 150 days.
Also, in repaying Alice's position, the repay() function uses msg.sender.
About that, yes there was a mistake in the steps, Alice will repay it, lowering all other rewards. This is a simpler sandwich attack.
Hey @Slavchew, I did review your PoC carefully, in your PoC you said that the attacker will take 10m flashloan here:
gUSDC.mint(EXPLOITER, 10_000_000e18);
Following that, you used
warp
to warp 150 days before repaying the flashloan here:vm.warp(block.timestamp + 150 days); uint256 interest = _computeAliceLoanInterest(borrowTime, 100e18);
Could you please explain how can the exploiter do that in a real world scenario?
Hey @0xbtk, I did review the PoC again, and yes, you are right. I made a mistake in where the blocks should pass, but even if they pass before the Exploiter takes the flashloan, the result is the same, since as I wrote in #1026, staking is based on staked amount, not time.
Here's the fixed test, the Alice part can also be ignored because it's not used anywhere, I just forgot to remove it while writing the finding. Alice is not needed at all in the execution of the problem, Exploiter borrow, then wait, get the flashloan, stake, trigger notifyPnL, unstake, return the loan.
https://gist.github.com/Slavchew/acff9a9c094d475b5d4806901eb61b64
Also, in repaying Alice's position, the repay() function uses msg.sender.
Regarding that, the repay function uses msg.sender, but this is the repayer, as I mentioned if you create the attack with 2 accounts like in the PoC steps, you need to transfer your credit tokens to the Exploiter to pay off your loan and trigger notifyPnL().
Let me know if you have any other concerns, but I'm sure the problem is valid.
Lines of code
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L114-L155 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L158-L212 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L216-L290
Vulnerability details
Impact
A malicious borrower can reduce all Guild holders’ rewards with a big flashloan, upon full repayment.
Borrower opens a loan with the minimum borrowable amount in term with no mandatory partial repayment. Wait for the loan to accumulate interest, then transfer the funds to an alternative account - EXPLOITER. EXPLOITER takes a flash loan, stakes the amount through
SurplusGuildMinter
, repays the original loan, lowering the rewards for all other GUILD holders by increasing the_gaugeWeight
with his staked tokens. Afterward, the EXPLOITER unstakes and returns the flashloan.The attacker ends up with his collateral, his loan repaid, credit reward and a big percentage of the reward intended for
GUILD
holders since he is the largest staker for this term at the time of the attack. He receives significant credit and guild rewards as a staker, which is incorrect because he was a staker only for that specific transaction.He only pays the unavoidable interest payment, typically around $4-5, along with gas costs in the scenario described below.
Proof of Concept
The exploiter has two accounts:
notifyPnL()
.Here are the detailed steps to execute the described attack:
PSM
.notifyPnL()
.notifyPnL()
updates the_gaugeProfitIndex
, reducing rewards for other Guild holders.Coded PoC
Create new file in
test/unit/loan
calledDeflateGuildHoldersRewards.t.sol
There are tests for both cases – one without the attack and another with the attack scenario.
Run them with:
Tools Used
Manual
Recommended Mitigation Steps
Providing recommendations is challenging due to the large codebase, and code changes might affect other parts of the system.
The things we came up with to protect against this are: to not allow staking and unstaking in the same block, implementing staking/unstaking fee, or implementing a "warm-up period" during which stakers are unable to accumulate interest.
We are open to collaborate with the development team to find a proper mitigation for the problem.
Assessed type
Context