Closed sherlock-admin2 closed 1 month ago
Quoting the submitter, "This error fundamentally misunderstands the flow of funds in a liquidation event". The keeper receives collateral which is expected to be sold for dai. That dai is then pulled from the keeper in order to repay the vault's debt.
Squilliam
High
Incorrect DAI Transfer in
LockstakeClipper
will Drain Funds from KeepersSummary
Incorrect Dai transfer in the
LockstakeClipper::take
function will cause a loss of funds for keepers as the system will transfer Dai from the keeper instead of from the Vat during liquidations.Root Cause
The root cause of this vulnerability lies in the
take
function of theLockstakeClipper
contract. Specifically, there is an incorrect implementation of the Dai transfer mechanism during the liquidation process.The specific line causing the vulnerability is:
This line is instructing the Vat (the core accounting system of MakerDAO) to move Dai from
msg.sender
(the keeper/liquidator) to the vow (the system surplus). This is incorrect because:The correct implementation should move Dai from the Vat itself (represented by address(this) in the context of the LockstakeClipper) to the vow. This would properly account for the Dai being recovered from the liquidated position without penalizing the keeper.
This error fundamentally misunderstands the flow of funds in a liquidation event. Instead of the system (Vat) paying off the debt and incentivizing the keeper, it's incorrectly taking funds from the keeper, which completely inverts the intended economic model of the liquidation system.
This can be found here: https://github.com/sherlock-audit/2024-06-makerdao-endgame/blob/main/lockstake/src/LockstakeClipper.sol?=plain#L405
Internal pre-conditions
External pre-conditions
None
Attack Path
Impact
Immediate Financial Losses for Keepers: Keepers (liquidators) who participate in auctions will suffer direct financial losses instead of earning rewards. For example, if a keeper participates in the liquidation of a 100,000 DAI debt position, they could lose the amount of DAI they bid in the auction. This could range from a few thousand DAI to potentially the full auction amount, depending on their bidding strategy and the auction parameters. This could lead to substantial losses for active keepers, potentially in the millions of DAI if multiple large liquidations occur before the issue is detected.
Breakdown of the Liquidation Mechanism: Once keepers realize they're losing money, they will stop participating in liquidations. This will leave the system unable to process bad debt, leading to an accumulation of undercollateralized positions. If left uncorrected, this vulnerability could severely compromise the protocol's liquidation mechanism, which is crucial for maintaining system health. Over time, this could indirectly challenge the protocol's ability to maintain the DAI peg and overall stability.
Systemic Imbalance in the Maker Protocol: The incorrect movement of DAI will cause a mismatch between the protocol's debt and its collateral backing. This vulnerability could lead to a breakdown of the liquidation mechanism as keepers would be disincentivized from participating. Over time, if left uncorrected, this could result in an accumulation of undercollateralized positions in the system, potentially putting pressure on the DAI peg.
PoC
Create a new test file and add the following code:
PipMock.sol:
StairstepExponentialDecreaseAbstract.sol - imported from the lockstake library.
GemMock.sol:
NstJoinMock.sol:
MkrNgtMock.sol:
Run these tests with the following:
forge test --mt testArithmeticInKickAndTake -vvv --via-ir
forge test --mt testKeeperLossesDuringLiquidation -vvv --via-ir
forge test --mt testKeeperEconomicLosses -vvv --via-ir
forge test --mt testTakeFunctionRootCause -vvv --via-ir
forge test --mt testLiquidationAmountDiscrepancy -vvv --via-ir
forge test --mt testTakeFunctionAccountingDiscrepancy -vvv --via-ir
The tests prove the following:
testArithmeticInKickAndTake
: This test shows that after a liquidation, the keeper's DAI balance decreases by 82.5 DAI, while the Vow's balance increases by the same amount. This proves that DAI is being transferred from the keeper to the Vow, instead of from the system to the Vow.testKeeperLossesDuringLiquidation
: Similar to the first test, this explicitly shows the keeper's balance decreasing by 82.5 DAI and the Vow's balance increasing by the same amount. This directly demonstrates the incorrect fund transfer.testKeeperEconomicLosses
: This test focuses on the keeper's balance before and after the liquidation. It shows that the keeper starts with 10,000 DAI and ends with 9,917.5 DAI, losing 82.5 DAI in the process. This proves that keepers are losing money by participating in liquidations.testTakeFunctionRootCause
: This test isolates the issue to the take function. It shows the exact DAI movement: 82.5 DAI from the keeper to the Vow. This pinpoints the location of the vulnerability in the code.testLiquidationAmountDiscrepancy
: This test demonstrates a larger discrepancy. The keeper loses 100 DAI, which is transferred to the Vow. This shows that the amount transferred can vary, but it's always coming from the keeper instead of the system.testTakeFunctionAccountingDiscrepancy
: This test reveals a critical accounting issue. It shows that while the Tab (debt) is reduced by 132 DAI, only 100 DAI is moved from the keeper to the Vow. This indicates that debt is being cleared without the corresponding DAI movement, creating an accounting imbalance in the system.How these tests prove the root vulnerability:
testTakeFunctionRootCause
, pinpoint the issue to the take function in theLockstakeClipper
contract.Mitigation
The primary mitigation is to correct the
take
function in theLockstakeClipper
contract. Replace the line:with something along the lines of:
This way the DAI is moved from the Vat (system) to the Vow, rather than from the keeper.
Accounting Verification: Add a check to ensure that the amount of DAI moved matches the debt reduction.