DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Valid

Withdrawing short's collateral more than expected

Summary

The ShortRecordFacet::decreaseCollateral() is vulnerable to front-running attacks, as the function calculates the Short's collateral ratio using a cached price, which can be front-run by attackers.

As a result, an attacker can withdraw collateral more than the actual (below the actual initialMargin threshold).

Vulnerability Details

A shorter can execute the decreaseCollateral() to decrease collateral (zETH) of their active Short position. After decreasing the collateral, the decreaseCollateral() will execute the LibShortRecord::getCollateralRatio() to calculate the Short's collateral ratio (cRatio). If the resulting cRatio is below the initialMargin threshold, the function will revert the transaction. In other words, the protocol does not permit a shorter to decrease their collateral below the initial margin.

However, the decreaseCollateral() is vulnerable to front-running attacks since the getCollateralRatio() will calculate the cRatio using a cached price retrieved from the LibOracle::getPrice().

Let's say Chainlink has updated the price to be higher than the protocol's oracle price (cached). An attacker (i.e., shorter) can front-run the protocol's oracle price update and execute the decreaseCollateral(). Since the protocol's oracle still retains the lower price (lower debt than the actual), the attacker can withdraw the collateral more than expected (below the actual initialMargin threshold).

// FILE: https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/ShortRecordFacet.sol
function decreaseCollateral(address asset, uint8 id, uint88 amount)
external
isNotFrozen(asset)
nonReentrant
onlyValidShortRecord(asset, msg.sender, id)
{
STypes.ShortRecord storage short = s.shortRecords[asset][msg.sender][id];
short.updateErcDebt(asset);
if (amount > short.collateral) revert Errors.InsufficientCollateral();
short.collateral -= amount;
@> uint256 cRatio = short.getCollateralRatio(asset);
@> if (cRatio < LibAsset.initialMargin(asset)) {
revert Errors.CollateralLowerThanMin();
}
uint256 vault = s.asset[asset].vault;
s.vaultUser[vault][msg.sender].ethEscrowed += amount;
LibShortRecord.disburseCollateral(
asset, msg.sender, amount, short.zethYieldRate, short.updatedAt
);
emit Events.DecreaseCollateral(asset, msg.sender, id, amount);
}
// FILE: https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/libraries/LibShortRecord.sol
function getCollateralRatio(STypes.ShortRecord memory short, address asset)
internal
view
returns (uint256 cRatio)
{
@> return short.collateral.div(short.ercDebt.mul(LibOracle.getPrice(asset)));
}

Impact

This vulnerability enables an attacker (i.e., shorter) to withdraw collateral more than the actual (below the actual initialMargin threshold), breaking the protocol's invariant, which can render the protocol's functions and algorithms malfunctioning.

Consequently, the vulnerability can indirectly increase bad debt to the Ditto protocol. The protocol can become insolvent, and the protocol's minted stable assets (e.g., cUSD) can eventually be de-pegged.

Tools Used

Manual Review

Recommendations

Since the cached oracle price is prone to front-running attacks, always execute the LibOracle::getOraclePrice() to get the accurate price from Chainlink.

Updates

Lead Judging Commences

0xnevi Lead Judge
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
serialcoder Submitter
over 1 year ago
0xnevi Lead Judge
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-567

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.