58628 sc high attackers can avoid redemption losses by temporarily burning and re borrowing the debt

Submitted on Nov 3rd 2025 at 17:39:56 UTC by @XDZIBECX for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58628

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol

  • Impacts:

    • Theft of unclaimed yield

Description

Brief/Intro

there is an issuse in this contract in the opeation of how it distributes the redemption losses among the CDP holders, because When a redemption occurs, the protocol uses a weight-based formula to fairly split the collateral loss across all users based on their debt positions, but an attacker can exploit a missing time-lock and make a manipulation, attackers can temporarily burn (repay) half of their debt right before a large redemption happens, and this is makes the protocol think there's less total debt than there actually is. so as result this will cause the protocol to calculate a higher loss percentage for everyone. But since the attacker has temporarily reduced their debt, they experience this higher percentage on a smaller amount, resulting in less loss. and after the redemption, the attacker re-borrows the same amount they burned. this is gone result that the attacker pays only 60-70% of their fair share of the redemption costs, while the users pay 130-140% of theirs. check the poc i use a scenarion confirm this , so this is allows attacker to steal 1,666 tokens from another user on a single 10,000 token redemption this need to be fixed ..

Vulnerability Details

the root of the bug is the mint is miss a check that is prevente users from minting in the same block after burning, the burn fucntion is blocks the burning after minting in the same block; and this asymmetry it's enables a temporarily reducing debt before a redemption to manipulate the global weight calculation and reduce the attacker's losses while increasing others' losses.

the vulnerability is came from this interconnected code here, the burn function is prevents the same-block repayment after minting from this --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L453C1-L496C1

    function burn(uint256 amount, uint256 recipientId) external returns (uint256) {
        _checkArgument(amount > 0);
        _checkForValidAccountId(recipientId);
        // Check that the user did not mint in this same block
        // This is used to prevent flash loan repayments
        if (block.number == _accounts[recipientId].lastMintBlock) revert CannotRepayOnMintBlock(); 

but there is a missing of Time-Lock in the mint Function the fucntion doesn't check if the user just burned debt or not here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L417C1-L433C6 :

  • and the Account struct confirms this because it only tracks lastMintBlock, not lastBurnBlock here ---> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/interfaces/IAlchemistV3.sol#L52 : so this a problem because users can burn debt and immediately mint it back in the next block.

  • this state of manipulation it's happen as this , when a user burns debt, the _subDebt function is reduces two values ---> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L932C1-L953C6 :

and this reductions are immediate and global, so when an attackers is burns debt, they reduce, their own rawLocked and the global _totalLocked , so When a redemption is happens, it calculates a "weight" to determine how much collateral each user should lose. and this weight is based on the global _totalLocked here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L631C9-L635C1 :

the weight formula is---> WeightIncrement(amount, total) = -log2((total - amount) / total) so when the total is smaller, the weight is larger.this is means, the normal redemption with _totalLocked = 10,000 is creates a weight of ~0.152 , and the manipulated redemption with _totalLocked = 9,500 is Creates weight of ~0.160 (higher!)

  • When users sync to apply the redemption loss, the calculation uses their current rawLocked on this line --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L1045C8-L1047C57 :

Impact Details

i use this as high because the attack that can came from this bug is can enables attackers to steal collateral from other CDP holders during the redemption, so time a significant redemption occurs in the Transmuter, then a malicious users can front-run it by temporarily reducing their debt position, and cause the redemption weight calculation to be based on deflated global locked collateral. and this is create a loss, and attackers can avoid 30-40% of their fair share of redemption losses, with that burden shifted to honest users who pay 30-40% more than they should. as an example i use in the test result on a 100,000 token redemption, an attacker save approximately 16,666 tokens while other users collectively lose that same amount extra a direct wealth transfer.

References

i use all on the vulnerability details

Proof of Concept

Proof of Concept

here is a test show and confirm the isssue copy past this in the and run it as

  • the result are show as :

Was this helpful?