
Date: August 28, 2026 (team statement August 29)
Chain: Ethereum
Loss: about $775K across seven Ajna v2 pools, per Defimon
Class: liquidation accounting that let one party settle an auction for more than it was owed
At 16:19 UTC on August 28, 2026, a transaction in Ethereum block 25,854,888 took about 48 cbETH out of an Ajna v2 lending pool and paid about 3.47 WETH of debt to get it. Ajna has no price oracle and no admin key, so there was no price feed to manipulate and nothing to switch off. The pool's own liquidation functions paid out the collateral, step by step, in the order the attacker called them.
By the time Defimon published its pool-by-pool accounting the next morning, seven Ajna v2 pools had lost about $775,000 in total. Ajna's team had asked users at 04:58 UTC on August 29 to withdraw their quote tokens, repay their loans and stop interacting with the protocol, because an immutable contract can't be paused. The attacker had stood on both sides of a liquidation, as the borrower being liquidated and as the lender and taker doing the liquidating, and the pool's books let that one party walk away with more collateral than the debt it cleared was worth.
Ajna is a permissionless, peer-to-pool lending protocol built to work without external price feeds. Lenders deposit quote tokens into price buckets, effectively bids that say what they'll lend against the collateral at a given price, and borrowers post collateral and draw debt against those buckets. Every price the pool uses, including the prices in its liquidation auctions, comes from the pool's own bucket state.
When a loan goes bad, someone calls kick to start a liquidation auction, posting a bond. Takers then buy the collateral either with their own quote tokens through take, or with quote tokens sitting in a lender bucket through bucketTake, which credits that bucket with collateral in exchange. Once the auction is finished, settle closes the books, writing off any remaining debt against lender deposits. Ajna v2 had been audited, and CryptoTicker notes it was built to be immutable, with no governance body, no upgrade path and no administrator key that could halt it.
Dropping the oracle removes oracle manipulation from the threat model and puts the full weight of correctness on the pool's internal accounting. Every route a liquidation can take through kick, take, bucketTake and settle has to leave collateral, debt and lender deposits in balance, because the pool has no outside price to sanity-check itself against. The DeFiHackLabs reproduction states the consequence directly: since auction and bucket prices come only from the pool's own state, an attacker who controls both the borrower and the taker shapes the prices those two sides meet at, and the loss lands on the pool's other lenders.
The setup was in place about an hour before the extraction. Ajna's post-mortem, published on September 15, describes a single setup transaction. The attacker deposited about 49.34 WETH into a lending bucket priced far above market, and a first position, Loan A, borrowed about 49.32 WETH against roughly 0.00105 cbETH, which the pool accepted because the attacker's own bucket set the price. In the same transaction a linked helper contract opened Loan B, which also borrowed about 49.32 WETH but against about 48.13 cbETH of real collateral, close to the liquidation line without crossing it. Kicking Loan A added its penalty to the pool's aggregate debt, which moved the Lowest Utilized Price, the pool-wide boundary every loan is judged against, and that made Loan B liquidatable even though nothing about Loan B had changed. DeFiHackLabs, which replays the extraction from that state, puts the parked deposit in bucket 2000 of the cbETH/WETH pool. Every function used after that, bucketTake, take, removeCollateral and repayDebt, is permissionless.
The Crypto Times reconstructed the extraction, transaction 0x12df...e4f5. The attack contract flash-loaned 4 WETH from Balancer and called bucketTake on the borrower's auction, which spent the attacker's own bucket deposit to repay most of the debt, cutting it from 49.36 to 3.47 WETH, and awarded the taker all of the bucket's LP while the kicker received none. A removeCollateral call burned that LP and pulled 1.51 cbETH out of the pool, which the attacker swapped on Uniswap v3 for 1.72 WETH sent to an externally owned account.
The attack contract then called take on the rest of the auction, paying 3.47 WETH for 0.106 cbETH and clearing the residual debt. It finished with repayDebt from the borrower's side with quoteRepaid set to zero, which released the remaining 46.51 cbETH of now-unencumbered collateral to the taker. After repaying the flash loan, the attack contract held 43.75 cbETH, about $121,000, and DeFiHackLabs puts the net for this pool at about $124.8K. Across the transaction, the pool gave up about 48.13 cbETH, roughly $133,000.
Every price in that sequence came from buckets and an auction the attacker had set up, so the pool had nothing outside itself to tell it the trade was lopsided. Picture an estate sale where the same person can set the starting price, place the winning bids and sign off on the final ledger. Ajna's own analysis names two issues that were harmless alone and damaging together: one loan's liquidation could change another loan's liquidation status, and the liquidity that made one loan possible could be spent through another loan's auction. The deposit used to pay down Loan B had already been lent out to Loan A, yet it still sat on the books at full value, and the post-mortem says closing only one of the two gaps wouldn't have fully fixed this class of exploit.
According to The Crypto Times, the attack contracts were deployed at about 15:16 UTC on August 28, with preparation funding routed through Tornado Cash, and the first extraction landed 63 minutes later. The same pattern then ran through six more pools. Defimon's breakdown put the losses at about $173,700 from syrupUSDC, $159,800 from wstETH, $143,000 from rETH in two extractions, $136,900 from cbETH in two transactions, $101,800 from WBTC, $42,000 from WETH/USDC and $18,000 from sDAI, and DeFiHackLabs lists the same split. Defimon's stated total was about $775,400 and Cryptopolitan, citing DefiLlama, gave about $775,000. Ajna's post-mortem cites a public estimate of about $797,322 across the seven Ethereum pools, notes that it hadn't reconstructed every pool itself, and says pools on Base were hit through the same gap as well.
Ajna's team posted its first public acknowledgement at 04:58 UTC on August 29, describing an investigation into unusual movements and asking users to exit. Defimon published its breakdown at 09:29 UTC and said its monitoring had spotted the prepared attack more than an hour before the first extraction and alerted the team on Discord without a response. Both CryptoTicker and The Crypto Times present that as Defimon's own unverified claim. With no pause available, users' own withdrawals were the only way to protect remaining deposits, and CryptoTicker put Ajna's total value locked after the incident at about $246,880. Because v2 can't be patched, Ajna's post-mortem says the fix will ship as a new deployment, v3, with liquidation accounting that has been corrected and independently reviewed before launch.
Olympix reads a protocol the way an attacker does, as states, transitions and entry points, and takes in its design, here Ajna's oracle-free auctions and bucket accounting, to infer the invariants it relies on but never writes down. For an Ajna pool the invariant reads simply: whatever sequence of take, bucketTake, removeCollateral and repayDebt closes an auction, the collateral that leaves the pool can't be worth more than the debt actually retired, judged at prices no single account controls.
An oracle-free pool has a huge space of call sequences, but it isn't unbounded. The engine maps the paths through the liquidation functions, keeps the feasible ones and narrows to those that can touch the invariant using signals. Olympix's static analysis surfaces the signal in this case: liquidation paths that close out collateral and debt in different places, where one route credits bucket LP to the taker, another lets collateral out against a zero repayment, and nothing stops the borrower, the bucket's depositor and the taker from being the same party. A kick that changes the pool-wide price every other loan is measured against is part of the same signal, since it lets one position change another's status without touching it. Olympix has flagged inconsistent bad-debt accounting across liquidation paths before in other codebases.
BugPoCer picks up that flagged path with Counterexample-Guided Invariant Refutation. It treats borrower, depositor, kicker and taker as roles one attacker can hold at once, and it searches chains of liquidation calls for the first one that leaves the pool's books short. The counterexample matches the August 28 sequence: a kick on a thinly collateralized loan that tips a second loan into liquidation, a bucketTake that repays the debt from the attacker's own bucket and hands the taker its LP, a removeCollateral that cashes that LP out, a take that clears the remainder, and a zero repayment that releases the rest of the collateral, returned with the minimal conditions that trigger it and a generated Forge test that replays it.
Ajna v2 can't be upgraded or paused, so any fix had to land before the pools went live. Olympix runs in development, at every merge and before release, which puts the counterexample in front of the team while a fix is still a code change.
Ajna's design traded oracle risk for a stricter demand on its own math, and every liquidation route has to honor the same balance between collateral out and debt retired, even when one address plays every role. For builders of immutable lending pools, those routes need to be tested as combinations an attacker can chain together, long before the code is frozen on mainnet. To see how Olympix explores the liquidation paths in your own protocol and hands back a runnable counterexample for any that leak value, book a technical walkthrough.
Protocol: Ajna v2 (seven ERC20 pools: syrupUSDC, wstETH, rETH, cbETH, WBTC, WETH/USDC, sDAI)
Chain: Ethereum
Date: August 28, 2026; attack contracts deployed about 15:16 UTC, first extraction 16:19 UTC
Loss: about $775K, per Defimon (about $124.8K from the first cbETH pool transaction, per DeFiHackLabs)
Attacker EOA: 0x6F2f...827e
Attacker-controlled borrower position: 0x02D3...6F5f
Victim pool (cbETH/WETH ERC20Pool): 0xad24...178e
ERC20Pool implementation: 0x05bB...9A21
First exploit tx (cbETH pool): 0x12df...e4f5
Block: 25854888
DeFiHackLabs: AjnaFinance exploit proof of concept
The Crypto Times: Ajna v2 Loses $775K on Ethereum After Attacker Exploits Liquidation Math
CryptoTicker: Ajna Exploit: $775,400 From Seven ETH Pools