OAK — OnChain Attack Knowledge

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

Loss
~$7.3M across 117 accounts, drained from users who had approved Exactly's DebtManager periphery contract. The top ten victims accounted for 84% of the total and the largest single loss was nearly $1.8M. A copycat repeated the attack about three hours later for a further ~$260K. Exactly paused the protocol within hours, set all Markets' treasury fee to 0%, engaged Chainalysis to trace the funds, and offered a $700,000 bounty for information leading to the attacker's arrest and recovery. Same-day reporting ranged from $7.3M to $12M as estimates settled; the $7.3M figure is the one the protocol and subsequent analyses use.
OAK Techniques observed
OAK-T9.004 (Access-Control Misconfiguration — primary, in the input-validation shape rather than the permissions shape. DebtManager.leverage() accepted a market address and a permit as caller-supplied arguments and validated neither. The attacker passed a market contract of their own and a forged permit, and the contract acted on the victim's behalf because nothing checked that it should. See techniques/T9.004-access-control-misconfiguration.md). OAK-T9.005 (Reentrancy — the fake market's deposit() called back into crossDeleverage(), and that re-entry is what moved real funds out of the legitimate USDC markets; proceeds were routed through attacker-controlled Uniswap pools, with health-factor manipulation used to trigger liquidations and extract more. See techniques/T9.005-reentrancy.md).
Attribution
pseudonymous. On-chain identifiers only. Exactly engaged Chainalysis for tracing and offered a $700,000 bounty; no individual has been publicly named.
Key teaching point
A contract that takes "which market?" as a parameter has made the caller an authority on what its own protocol consists of. Every other defect here follows from that one. The permit forgery worked because the fake market was trusted to interpret it; the reentrancy worked because the fake market got control during a state-sensitive path; the approvals were spendable because the contract holding them would talk to any address handed to it. The control is a registry check: a periphery contract must resolve market addresses against the protocol's own canonical list and reject anything else, before any external call and before any permit is consumed. The second, organisational lesson is the one Exactly itself acted on — the periphery was outside the audit scope. DebtManager was a convenience wrapper, not core lending logic, so it was not in the reviewed set, and it was also the contract holding everyone's approvals. Audit scope follows the architecture diagram; attackers follow the approvals.

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 Approval logs 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 require and 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; DebtManager was 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

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.

Techniques demonstrated (2)