Crypto exploits

MALT exploit analysis

The MALT/DAI sLP ran its below-peg treasury recovery inside swap(), then checked its constant-product invariant against balances that included the just-injected treasury DAI, so a 1 DAI input bought a MALT out backed by ~6,287 DAI from the Malt Capital Source.

David

Security ·

Reported loss
12,744.64 DAI drained from the Capital Source treasury across two recovery injections; attacker realized 13,440.58 DAI net after QuickSwap arbitrage, per DeFiHackLabs
Occurred
Logged
Chain
Polygon

Logged as “2026-10-03: The Maltdai Slp Runs Its Below Peg Price Recovery Inside S”.

Analysis2 min read

The Malt Stablecoin's MALT/DAI liquidity pool ran its below-peg rebalance inside swap rather than before it, then checked its constant-product invariant against post-rebalance balances without separating the injected treasury DAI from the caller's input. On 3 October 2026, an attacker on Polygon paid 1 DAI for a swap that the pool's own recovery routine padded with ~6,287 DAI from the Malt treasury, then repeated the cycle. DeFiHackLabs records a treasury drain of 12,744.64 DAI across the two recovery-triggered swaps (with the attacker realizing 13,440.58 DAI net after the QuickSwap arbitrage legs); the README entry notes that a ~$72K figure from an earlier alert does not match the on-chain reconstruction.

The vulnerable contract is the sLP at 0xfe6C096a2871337d4f6F7DD04Ebda733E94D7A13, a UniswapV2-style pair with a custom swap(uint256,uint256,address) selector (0x6d9a640a). When swap is called with no input and amount1Out > 0 (a 1 DAI buy of MALT out), the pair reads its own pre-swap reserves, then runs its below-peg recovery before sending the output. The recovery is a withdrawal from Malt's Capital Source treasury into the same pair, which swap then treats as additional token1 for the constant-product check.

The on-chain sequence

The exploit is a single transaction (0x915eccb...e8445b, block 94,869,210, Polygon). The attacker funded the cycle with a Balancer Vault flash loan of 1,300 DAI, then:

  • Bought MALT on QuickSwap (0xa5E0...78ff) with 1,300 DAI, transferred that MALT into the sLP, and called swap(0, 6,459,541e15, attacker) to sell it back, pushing the sLP's MALT price below peg.
  • Transferred 1 DAI from the attacker's contract into the sLP and called swap(7,823,544e15, 0, attacker). The pair ran its recovery, which transferred 6,286.10 DAI from the Capital Source into the pool. The constant-product check then passed: the pool held the attacker's 1 DAI plus the treasury's 6,286 DAI, and the invariant released a 7,823,544e15-wei MALT out as if the caller had supplied all of it.
  • Sold the released MALT back on QuickSwap for DAI, then ran a second round of the same shape (treasury injection of 6,458.54 DAI), and a third smaller round whose invariant check passed without needing recovery.

The sLP's only invariant is k = balance0 * balance1 measured after both the caller's transfer and the treasury's deposit have landed; both numbers feed into k together, with no separation between them. The PoC reproduces the exact per-leg amounts and asserts an attacker profit of 13,440.58 DAI to the wei, matching the on-chain log at index 94.

What was missing

swap should have either separated the deposit side into the caller's transfer vs the recovery's injection before the invariant check, or moved the recovery outside the swap's call frame. The simplest fix is to snapshot balanceOf(pool) for the input token before the recovery fires and compute the constant-product check against that snapshot, ignoring the recovery injection. The sLP was deployed with the recovery inside swap on the assumption that any meaningful input would be the caller's, not the treasury's; that assumption is the bug.

Sources read (3)

Other exploits

Query every incident yourself — free, no signup

Updated 11 Nov 2026. Figures are as reported by the sources listed above and are not independently verified.