Inverse Finance contest - Ch_301's results

Rethink the way you borrow.

General Information

Platform: Code4rena

Start Date: 25/10/2022

Pot Size: $50,000 USDC

Total HM: 18

Participants: 127

Period: 5 days

Judge: 0xean

Total Solo HM: 9

Id: 175

League: ETH

Inverse Finance

Findings Distribution

Researcher Performance

Rank: 5/127

Findings: 2

Award: $3,554.12

🌟 Selected for report: 1

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: Ch_301

Labels

bug
2 (Med Risk)
satisfactory
sponsor confirmed
selected for report
M-12

Awards

3397.8465 USDC - $3,397.85

External Links

Lines of code

https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L566

Vulnerability details

Impact

Users abels to invoke forceReplenish() when they are on liquidation position

Proof of Concept

On Market.sol ==> forceReplenish() On this line

uint collateralValue = getCollateralValueInternal(user);

getCollateralValueInternal(user) only return the value of the collateral

function getCollateralValueInternal(address user) internal returns (uint) { IEscrow escrow = predictEscrow(user); uint collateralBalance = escrow.balance(); return collateralBalance * oracle.getPrice(address(collateral), collateralFactorBps) / 1 ether;

So if the user have 1.5 wETH at the price of 1 ETH = 1600 USD It will return 1.5 * 1600 and this value is the real value we can’t just check it directly with the debt like this

require(collateralValue >= debts[user], "Exceeded collateral value");

This is no longer over collateralized protocol The value needs to be multiplied by collateralFactorBps / 10000

  • So depending on the value of collateralFactorBps and liquidationFactorBps the user could be in the liquidation position but he is able to invoke forceReplenish() to cover all their dueTokensAccrued[user] on DBR.sol and get more DOLA
  • or it will lead a healthy debt to be in the liquidation position after invoking forceReplenish()

Use getCreditLimitInternal() rather than getCollateralValueInternal().

#0 - 0xean

2022-11-05T22:08:42Z

I believe this warden may be correct in the fact that we should actually be adding the collateralFactor into the check

#1 - 08xmt

2022-11-14T13:22:33Z

While increasing debt beyond the Credit limit do risk creating bad debt, this bad debt is owed entirely to the protocol. If one wanted to minimise the amount of bad debt created this way, it would be possible to change the line to getCollateralValueInternal() * (10000 - liquidationIncentiveBps) / 10000;, as this would also slightly reduce the amount of bad debt paid out to force replenishers as incentives.

#2 - c4-sponsor

2022-11-14T13:22:43Z

08xmt marked the issue as sponsor disputed

#3 - c4-sponsor

2022-11-15T12:28:02Z

08xmt marked the issue as sponsor confirmed

#5 - c4-judge

2022-11-28T19:35:24Z

0xean marked the issue as satisfactory

#6 - c4-judge

2022-12-01T15:54:25Z

0xean marked the issue as selected for report

Findings Information

🌟 Selected for report: rbserver

Also found by: 0xRobocop, Ch_301, ElKu, Jeiwan, MiloTruck, Picodes, sam_cunningham

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-583

Awards

156.2673 USDC - $156.27

External Links

Lines of code

https://github.com/code-423n4/2022-10-inverse/blob/main/src/DBR.sol#L313-L317 https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L531-L539

Vulnerability details

Impact

The users could repay() their debt of DOLA without having any amount of DBR on their balance

Proof of Concept

When user repays his debt. he needs to pay DBR as an interest which is tracked on dueTokensAccrued[user] Let’s say Alice has a 300 DOLA debt. After T amount of time he invokes repay() on Market.sol

function repay(address user, uint amount) public { uint debt = debts[user]; require(debt >= amount, "Insufficient debt"); debts[user] -= amount; totalDebt -= amount; dbr.onRepay(user, amount); dola.transferFrom(msg.sender, address(this), amount); emit Repay(user, msg.sender, amount); }

This line

dbr.onRepay(user, amount);

Will execute on DBR.sol

function onRepay(address user, uint repaidDebt) public { require(markets[msg.sender], "Only markets can call onRepay"); accrueDueTokens(user); debts[user] -= repaidDebt; }

And on

accrueDueTokens(user)

Just calculate the interest But there is no check if the user is actually has this amount of DBR

In case repay all the debts, you need to check if the user has a deficit. Here you can revert repay()or invoke onForceReplenish() or accept the repay with liquidate() some collateral to pay the deficit

#0 - c4-judge

2022-11-05T18:44:09Z

0xean marked the issue as duplicate

#1 - Simon-Busch

2022-12-05T15:15:41Z

Marked satisfactory as requested by @0xean

#2 - c4-judge

2022-12-06T00:03:05Z

0xean changed the severity to 2 (Med Risk)

#3 - c4-judge

2022-12-07T08:16:07Z

Simon-Busch marked the issue as duplicate of #583

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter