The MarginCallSecondaryFacet::liquidateSecondary()
is vulnerable to front-running attacks, as the function calculates the buy-back's collateral amount using a cached price, which can be front-run by attackers.
As a result, an attacker can execute the liquidateSecondary()
to liquidate Short positions and receive the buy-back's collateral amount more than they should.
The liquidateSecondary()
liquidates the Short positions (batches) using the liquidator's ERC (cUSD) to buy back the liquidated Shorts' debt. The liquidator will receive the collateral (zETH) for exchange.
The function will call the _secondaryLiquidationHelper()
to perform the full liquidation on target Short positions and call the _secondaryLiquidationHelperPartialTapp()
to perform the partial liquidation on the TAPP's Short positions.
After liquidating each Short position, the buy-back's collateral amount that the liquidator will receive will be accumulated by the variable liquidatorCollateral
. The liquidator will receive the total accumulated collateral after the loop of liquidating Short positions finishes.
However, the liquidateSecondary()
is vulnerable to front-running attacks. Both the _secondaryLiquidationHelper()
and _secondaryLiquidationHelperPartialTapp()
calculate the buy-back's collateral amount using a cached price retrieved from the LibOracle::getPrice()
in L167 - L168 and L204 - L205, respectively.
Let's say Chainlink has updated the price to be lower than the protocol's oracle price (cached). An attacker can front-run the protocol's oracle price update and execute the liquidateSecondary()
to liquidate Short positions. Since the protocol's oracle still retains the higher price (higher debt than the actual), the attacker will receive the buy-back's collateral amount more than expected.
The liquidateSecondary() executes the _secondaryLiquidationHelperPartialTapp()
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L97
The liquidateSecondary() executes the _secondaryLiquidationHelper()
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L100
The liquidateSecondary() updates the accumulated liquidatorCollateral
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L104
The liquidator receives the total collateral (zETH) according to the accumulated liquidatorCollateral
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L115
The _secondaryLiquidationHelper() calculates the ercDebtAtOraclePrice using the cached price (which the attacker can front-run its update)
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L167-L168
The calculated ercDebtAtOraclePrice is assigned to the m.liquidatorCollateral
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L169
The more ercDebtAtOraclePrice, the less collateral the remainingCollateralCollector will receive
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L176-L177
The _secondaryLiquidationHelperPartialTapp() calculates the m.liquidatorCollateral using the cached price (which the attacker can front-run its update)
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L204-L205
The TAPP's collateral will be reduced by the calculated m.liquidatorCollateral
: https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallSecondaryFacet.sol#L206
The attack from this vulnerability can increase more bad debt to both the liquidated shorters and the Ditto
protocol.
Specifically, the liquidated shorters and the protocol's TAPP Short positions will directly lose their collateral (zETH) to the attacker. Further, the protocol can become insolvent, and the protocol's minted stable assets (e.g., cUSD) can eventually become de-pegged.
Manual Review
Since the cached oracle price is prone to front-running attacks, always execute the LibOracle::getOraclePrice()
to get the accurate price from Chainlink.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.