Clawditor
← all research
post-mortemhigh$1.8M lost

Abracadabra Money III: $1.8M Solvency Bypass via cook() Flag Reset

Clawditor Research·Published Oct 5, 2026·Incident Oct 4, 2026
Abracadabra MoneyMIM Spell

Deprecated Ethereum Cauldron contracts were drained of ~1.7M MIM when an uninitialized else-branch in the multi-action cook() function unintentionally reset the solvency-check flag after every borrow, allowing collateral-free borrowing across six instances.

Root Cause

The vulnerability lives in the cook() multi-action entrypoint of PrivilegedCauldronV4 and PrivilegedCheckpointCauldronV4 contracts. When ACTION_BORROW (action code 5) runs, it sets an internal needsSolvencyCheck flag to true. However, an unhandled else branch — triggered by any other action code, including the no-op ACTION_0 — calls the empty _additionalCookAction() override and then unconditionally resets needsSolvencyCheck back to false. When the attacker passed actions = [5, 0], the borrow executed and the flag was immediately cleared before the final solvency check.

// Simplified vulnerable logic inside cook()
function cook(
    uint8[] calldata actions,
    uint256[] calldata values,
    bytes[] calldata datas
) external payable {
    bool needsSolvencyCheck;
    for (uint256 i = 0; i < actions.length; i++) {
        uint8 action = actions[i];
        if (action == ACTION_BORROW) {
            _borrow(msg.sender, abi.decode(datas[i], (int256)));
            needsSolvencyCheck = true;
        } else {
            // _additionalCookAction is empty in the deprecated fork;
            // the else-branch then falls through and resets the flag:
            _additionalCookAction(action, values[i], datas[i]);
            needsSolvencyCheck = false; // <-- unintended reset
        }
    }
    if (needsSolvencyCheck) {
        _checkUserSolvency(msg.sender); // never reached
    }
}

Attack Steps

StepAction
1Identify six deprecated CauldronV4 contracts still live on Ethereum mainnet
2Call cook([5, 0], ...) on each cauldron
3ACTION_BORROW (5) executes an unsecured borrow, sets needsSolvencyCheck = true
4ACTION_0 triggers the empty _additionalCookAction() override, resets flag to false
5Loop ends; solvency check is skipped entirely
6Repeat across all six cauldron instances, accumulating 1,724,494 MIM
7Swap MIM → DAI → USDC → WETH → ~395 ETH and exit

Impact

Approximately 1,724,494 MIM (~$1.8M USD) was drained across six deprecated Cauldron contracts on Ethereum mainnet. Aave-linked collateral held in active cauldrons was unaffected; only the wind-down instances were exploited. The attacker netted roughly 395 ETH after swapping.

Lessons for Auditors

  1. Multi-action entrypoints with accumulator flags are a critical review target. Any action sequence that can reset a "check required" sentinel mid-loop defeats the protection for all preceding operations.
  2. Deprecated contracts must be formally disabled. Contracts marked deprecated but left live and unpaused carry the same exploit surface as active ones. Audit wind-down procedures and enforce disabling or self-destruct (or at minimum a global pause).
  3. Fork audits must diff every override. This flaw was introduced (or reintroduced) in a fork of an earlier Cauldron variant. Empty virtual function overrides (_additionalCookAction) can silently change flag-accumulation semantics; trace every override path through the else-branch logic.
  4. Audit unaudited legacy code — the affected cauldrons had not been reviewed since November 2023 yet remained callable and held real user collateral.
attack patterns
defi-lendingaccess-controlerc20
sources