Worked example · 2023-08
Exactly Protocol — a periphery contract took the market address as an argument and never checked it, so a fake market reentered and spent 117 users' approvals — Exactly Protocol / Optimism — 2023-08-18
Explicitly not OAK-T4.004 (Allowance / Approve Drainer). The approvals the attacker spent were granted knowingly, to Exactly's own periphery contract, for its documented purpose. Nothing was phished. The detection surface is the contract's argument validation, not the user's signing behaviour — the same distinction the corpus draws for the 2026-09 ether.fi AtomicQueue case.
Summary
Exactly Protocol is a fixed-and-variable-rate lending protocol on Optimism. DebtManager is a periphery contract that composes multi-step positions on a user's behalf — leverage, deleverage, rollover — and to do that it holds ERC-20 approvals from the users who use those features.
Its leverage() function took the market address and a permit as arguments from the caller and validated neither. On 2023-08-18 an attacker called it with a market contract they had deployed themselves and a forged permit naming a victim. DebtManager proceeded to act for that victim. The attacker's fake market then used its deposit() callback to re-enter crossDeleverage(), and the re-entry pulled real assets out of Exactly's legitimate USDC markets and into pools the attacker controlled. Health-factor manipulation triggered liquidations that extracted further value.
The pattern repeated across 117 accounts for ~$7.3M, concentrated heavily — the ten largest losses were 84% of the total, the largest nearly $1.8M. About three hours later a copycat ran the same attack for roughly $260K more.
Exactly paused the protocol, zeroed treasury fees as a precaution, brought in Chainalysis to trace the stolen funds, posted a $700,000 bounty, and re-engaged its auditor ABDK for a fresh review that included the periphery contracts previously left out of scope.
Timeline (UTC, 2023-08-18 unless noted)
| When | Event | OAK ref |
|---|---|---|
| (standing) | DebtManager holds user ERC-20 approvals to compose leverage positions; it sits outside the audited contract set |
(standing exposure) |
| (standing) | leverage() accepts a market address and a permit from the caller and validates neither |
(latent T9.004 defect) |
| T+0 | Attacker calls leverage() with a self-deployed market contract and a forged permit naming a victim |
T9.004 exploitation |
| same transaction | The fake market's deposit() re-enters crossDeleverage(); real assets leave the legitimate USDC markets into attacker-controlled Uniswap pools |
T9.005 |
| repeatedly | Health factors manipulated to force liquidations and extract more; 117 accounts drained, ~$7.3M, top 10 = 84% | (extraction) |
| within hours | Exactly pauses the protocol, sets all Markets' treasury fee to 0% | (operator response) |
| T+~3h | Copycat attack repeats the pattern for a further ~$260K | (unpatched window) |
| after | Chainalysis engaged for tracing; $700,000 bounty announced; ABDK re-engaged to audit periphery contracts previously out of scope | (response and remediation) |
What defenders observed
- The victims did nothing on the day of the attack. Their exposure was set the last time they used the leverage feature and left an approval behind. As with the 2026-09 ether.fi AtomicQueue case, the blast radius equals the approval set of a contract that can be made to call arbitrary code — and that set is enumerable from
Approvallogs by anyone, including the protocol. - An unvalidated address argument is a complete exploit primitive on its own. No price manipulation, no flash loan, no governance, no key. The attacker supplied one of the protocol's own type signatures with an address of their choosing. A registry check — is this market in our canonical list? — is a single
requireand closes the whole chain, including the reentrancy, because attacker code never gets control. - A copycat landed three hours later, which says the window is measured in minutes not days. The mechanism was reconstructible from the first transaction. Any protocol relying on "we'll patch before anyone copies this" should note that the second attacker in this case needed less time than the incident call.
- Periphery contracts concentrate approvals and escape review at the same time. Core lending logic was audited;
DebtManagerwas a wrapper and was not. Wrappers are where UX convenience lives, which is exactly why users approve them for large amounts. Exactly's own remediation — re-auditing with periphery in scope — is the correct generalisation, and it is worth stating as a rule: scope the audit by where the approvals point, not by where the protocol logic sits.
Public references
[olympixexactly2023]— Olympix, "Exactly Protocol Lost $7.3M. The Code Worked. The Assumptions Didn't." (fake Market contract passed intoleverage(); forged permit; reentrancy via the fake market'sdeposit()intocrossDeleverage(); funds routed to attacker-controlled Uniswap pools; health-factor manipulation; 117 accounts, top 10 = 84%, largest ~$1.8M; ~$260K copycat three hours later; periphery contracts outside the original audit scope, re-audited by ABDK): https://olympix.security/blog/exactly-protocol-lost-7-3m-the-code-worked-the-assumptions-didnt[halbornexactly2023]— Halborn, "Explained: The Exactly Protocol Hack (August 2023)": https://www.halborn.com/blog/post/explained-the-exactly-protocol-hack-august-2023[cryptonewsexactlybounty2023]— CryptoNews, "Exactly Protocol Announces $700,000 Bounty For Information on Hacker" (2023-08-19 protocol disclosure: the DebtManager periphery contract was manipulated, the attacker passed in a malicious market contract; $7.3M): https://cryptonews.com/news/exactly-protocol-announces-700000-bounty-for-information-hacker/[defirektaugust2023]— De.Fi Rekt Report, "$29m Funds Lost in August 2023: Top DeFi Scams and Exploits": https://de.fi/blog/de-fi-rekt-report-29m-funds-lost-in-august-2023-top-defi-scams-and-exploits
Discussion
This entry is a correction, and the correction is the most useful thing in it. Until 2026-09-12 the corpus carried two files for this single incident, each asserting a mechanism that did not happen: 2023-08-exactly-protocol.md described a "cross-chain debt-ceiling bypass" via cross-chain message validation and mapped it to T10.002, and 2023-08-exactly-reinitialization.md described a "cross-contract reinitialization" of DebtManager and mapped it to T9.009. Exactly is not a cross-chain lending protocol, no cross-chain message was involved, and no initialisation guard was bypassed. Both files entered in the v0.1 bulk release (f32645a), both cited sources that exist nowhere — bib keys such as peckshieldexactly2023 (written in citation syntax, defined nowhere, carrying no URL) — and the T10.002 file closed its own Discussion with "the case closes the T10×2023 near-threshold gap at v0.1," which names the motive: a coverage cell needed an incident, and an incident was described to fit it.
The two files were surfaced by check_quality's near-duplicate-title detector, which had been reporting them since it was written and had never run in CI. That is the second lesson and it is about process rather than content: a linter nobody runs is a linter that finds nothing. check_quality and check_coverage are in npm run check as of this commit.
On the taxonomy itself, the corrected case is unremarkable and useful precisely for that — it is T9.004 in its input-validation shape chained to T9.005, the most ordinary pair in the corpus, in a contract that was not audited because it was considered peripheral. Its closest structural relatives are not other 2023 lending exploits but Gondi (2026-03-09) and ether.fi / Veda AtomicQueue (2026-09-11) — together the three anchors of the OAK-T9.004.001 forward candidate (standing-authorisation residue in periphery contracts): both are periphery contracts holding standing user approvals, both failed on a missing check about who or what the caller named, and in both the victims' exposure was created by a routine past interaction. Three years apart, same shape. Contributors documenting a periphery-contract incident should record what the contract was allowed to call and whose approvals it held, because those two fields, not the protocol's headline TVL, determine what such a defect is worth.