OAK — OnChain Attack Knowledge

Worked example · 2017-07

Parity Multisig Wallet — Ethereum — 2017-07-19 and 2017-11-06

Loss
combined ~663,774 ETH affected across both incidents (~150,000 ETH extracted in incident 1 + ~513,774 ETH frozen in incident 2; aggregate ~$330M at the time, ~$2.3B+ at 2024 ETH prices).
Loss (incident 1)
~150,000 ETH (~$30M at the time of the event; ~$300M+ at 2024 ETH prices) drained from three wallets (Edgeless, Swarm City, æternity) via initWallet re-initialisation. A subsequent White Hat Group rescue, using the same exploit primitive against the remaining vulnerable wallets, secured a further ~377,000 ETH and substantial token balances for return to legitimate owners.
Loss (incident 2)
~513,774 ETH frozen across ~587 wallets (~$300M at the time; ~$2B+ at 2024 ETH prices). Funds were not extracted — the library contract on which all post-July-20 Parity multisig wallets depended was destroyed via selfdestruct, rendering its dependent wallets permanently unable to transact. Funds remain inaccessible.
OAK Techniques observed
OAK-T9.004 (Access-Control Misconfiguration) — primary mechanistic classification for both incidents. The November freeze is a destruction-not-extraction sub-pattern of T9.004, distinct from the extraction sub-patterns that dominate the v0.1 example set.
Attribution
pseudonymous for both incidents. Incident 1 — pseudonymous attacker; never publicly identified. Incident 2 — self-identified GitHub user "devops199," who described the call sequence as accidental and surrendered the ownership trail publicly; OAK records the second incident as accidental destruction by a self-identified actor and does not speculate on identity beyond the public record.
Key teaching point
The 2017 Parity incidents have a singular role in EVM smart-contract security history. Together with The DAO 2016, they form the small set of cases that defined how the industry now thinks about pre-deployment controls: The DAO produced the checks-effects-interactions discipline and ReentrancyGuard; Parity produced the initialisation-at-deployment discipline and the proxy-pattern design rules that later became OpenZeppelin's Initializable, the EIP-1967 storage-slot conventions, and the transparent / UUPS proxy variants. Every modern upgradeable contract pattern carries this lineage in its design.

Two incidents documented in this file. Both are textbook OAK-T9.004 (Access-Control Misconfiguration) cases against the same contract family — the Parity multi-signature wallet — separated by approximately four months. The first was an extraction event; the second was an unrecoverable destruction event. Together they are the foundational historical anchor for the access-control vulnerability class on EVM chains, and in particular for the family of risks introduced by the delegatecall-to-shared-library deployment pattern.

Summary

Parity Technologies' multi-signature wallet was, from version 1.5 onward, structured as a thin per-user "stub" contract that forwarded all unmatched calls via delegatecall to a single shared library contract deployed on Ethereum mainnet. The library held the implementation logic; the stub held only state and a fallback that proxied calls into the library's address space. This pattern was an early ancestor of what later became the EIP-1967 / proxy-pattern industry standard, but in 2017 the discipline around how to deploy and protect the library half of such a pair did not yet exist.

Both 2017 Parity incidents are direct consequences of that missing discipline. In incident 1 (July 2017), the per-wallet stub forwarded calls into a library function — initWallet — that had no onlyUninitialised guard and could therefore be re-called post-deployment against an already-initialised stub. The attacker called initWallet against three high-value wallets, became the sole owner of each, then drained them. In incident 2 (November 2017), the library contract itself — not any stub — was the target. Because the library had been deployed but never initialised in its own state, calling initWallet directly on the library address established the caller as the library's owner. Once owner, the caller invoked the library's kill() function (selfdestruct). The library was destroyed; every dependent stub's delegatecall thereafter returned to empty bytecode; the wallets became permanently non-functional.

For OAK's purposes, both incidents are the canonical historical anchors for OAK-T9.004 on EVM. They are cross-referenced from the T9.004 Technique page and from any future Technique page that addresses proxy / library / delegatecall patterns.

Timeline (UTC)

When Event OAK ref
2017-07 (pre-event) Parity multisig wallet v1.5+ deployed; library contract holds implementation, per-user stubs forward via delegatecall T9.004 surface created
2017-07-19 Attacker calls initWallet on three high-value Parity multisig stubs (Edgeless, Swarm City, æternity), becoming sole owner of each; drains 150,000 ETH ($30M) in the same transaction sequence T9.004 extraction (incident 1)
2017-07-19 (within hours) White Hat Group, using the same initWallet re-call primitive, sweeps the remaining vulnerable wallets to attacker-controlled-but-cooperative addresses to prevent further black-hat extraction; ~377,000 ETH and substantial token balances secured for return (defensive counter-exploit)
2017-07-20 Parity publishes post-mortem; new multisig wallets deployed against a redeployed library contract; deployment of new multisig wallets temporarily suspended pending review (response)
2017-11-06 GitHub user devops199 calls initWallet directly on the redeployed library contract — which had never been initialised in its own state — becoming the library's owner T9.004 setup (incident 2)
2017-11-06 (same window) devops199 calls kill() on the library, invoking selfdestruct; library bytecode is removed from chain state T9.004 destruction (incident 2)
2017-11-06 onward All ~587 dependent Parity multisig wallets become non-functional; ~513,774 ETH frozen indefinitely (impact)
2017-11-08 Parity publishes second post-mortem acknowledging the library was deployed un-initialised (response)
2017-2018 Multiple proposals (EIP-156, EIP-867, EIP-999) raised in Ethereum governance to recover frozen funds via state edit; none adopted (governance)
2024+ Funds remain frozen; the case is recurrently cited in academic literature ([atzei2017survey], [zhou2023sok]) and in Solidity / proxy-pattern design discussions (legacy)

What defenders observed and learned

  • Library contracts must be initialised at deployment time, not "left for later." The November freeze's proximate cause was that the redeployed library was deployed but its own initWallet was never called by Parity. The library therefore had no owner of itself — and any caller could establish ownership simply by being first. The defender lesson, retroactively obvious and now universally taught, is that the constructor / initialiser of any contract that exposes ownership-conferring functions must run as part of the same transaction as the deployment, or the deployment is incomplete. Modern proxy patterns (OpenZeppelin's Initializable, transparent and UUPS proxy variants) embed this discipline directly into the inherited contract.
  • delegatecall to a shared library is a security boundary, not a code-reuse convenience. The 2017 Parity wallet was authored with a mental model of "the library is just a code source — the per-user wallet is the contract." This model is wrong: the library is itself a contract on chain with its own state, its own ownership, its own selfdestruct. Every function that the library exposes is a function the stub exposes via the fallback. The defender lesson is that contributors writing or auditing any delegatecall-based pattern must reason about the library half as a fully independent attack surface, including its own constructor / initialiser state and its own privileged functions. This is now the baseline framing in any reasonable Solidity introduction; in 2017 it was not.
  • selfdestruct is a privileged function and must be gated like one. The November freeze rests on the fact that the library exposed a kill() function that invoked selfdestruct and was reachable through the public initWallet-then-kill sequence. Modern industry practice — and OpenZeppelin's contract templates — have moved away from including selfdestruct at all, treating it as a footgun whose legitimate use cases are narrow and whose accidental-destruction risk is high. EIP-6049 (deprecation notice) and the post-Cancun behavioural change to SELFDESTRUCT (which no longer destroys contract code in most cases) are direct downstream artefacts of the lessons this incident forced into the ecosystem.
  • Destruction-not-extraction is a real failure mode and should be modelled explicitly. The November freeze is structurally unusual among major incidents: nothing was stolen. There was no attacker who walked away with funds, no laundering trail, no recovery target. The damage was that legitimate users could no longer access their own assets — permanently. Risk modelling that focuses only on adversarial-extraction scenarios understates loss exposure for any contract whose destruction (rather than capture) is reachable by an unauthenticated path. Defenders writing operational risk models for upgradeable systems should treat "the implementation can be destroyed" as a distinct, modellable scenario alongside "the implementation can be captured."
  • At-event detection is not the meaningful lever for either incident. Both extractions / destructions executed in single-digit transaction sequences; runtime monitoring in 2017 had no usable signal and would not have changed the outcome even if it had existed. Both incidents are pre-deployment-control failures: the defender lever that would have caught them is audit and formal verification of "can initWallet be called twice" and "can kill() be reached by an unauthenticated path." This is the same shape of conclusion as The DAO 2016 — an early-era extraction whose post-mortem moved the industry's defensive emphasis upstream of runtime detection.

What this example tells contributors writing future Technique pages

  • Single-Technique, multi-incident worked examples are valid. The Parity 2017 file documents two incidents under one Technique because the underlying mechanism — re-callable initWallet against an un-guarded delegatecall-reachable library function — is the same in both cases. Contributors writing future examples should not feel obliged to split a single mechanistic story across multiple files when the same protocol surface produces multiple incidents within a short window. The Parity case is the v0.1 model for this pattern.
  • Destruction-not-extraction is a worth-documenting sub-pattern of T9.004. OAK v0.1 records this pattern under T9.004 rather than splitting it out; a v0.x update may revisit if more cases accumulate. Contributors who encounter incidents in which a privileged function is destroyed rather than captured — and which therefore freeze rather than steal funds — should classify under T9.004 with explicit framing of the destruction sub-pattern. The November Parity freeze is the canonical reference.
  • Defensive counter-exploits are an established response category and should be named when they occur. The July 2017 White Hat Group rescue is the earliest large-scale instance of "good actors use the same exploit primitive to evacuate funds before bad actors can." It recurs at Wormhole 2022 (Jump Crypto's counter-exploit), at Curve 2023 (white-hat MEV-searcher rescues during the Vyper compiler incident), and elsewhere. Contributors writing examples that involve any white-hat / counter-exploit / cooperative-rescue dimension should describe the rescue mechanism explicitly rather than leaving it as an "and the rest was recovered" footnote — the rescue is itself a defensive technique with reusable structure.
  • Preserve the at-the-time / at-current-prices dual loss numbers for ETH-denominated 2017-era incidents. Parity's frozen ~513,774 ETH is ~$300M at November 2017 prices and ~$2B+ at 2024 ETH prices; both numbers are correct only with their qualifier. This convention follows the precedent established for The DAO (/Users/dmytro.chystiakov/Projects/oak/examples/2016-06-the-dao.md) and applies to any pre-2018 ETH-denominated worked example.

Public references

  • Parity Technologies. Security Alert. Parity blog, 2017-07-19 — [parityjuly2017postmortem]. Parity's contemporaneous post-mortem of the July initWallet extraction.
  • Parity Technologies. A Postmortem on the Parity Multi-Sig Library Self-Destruct. Parity blog, 2017-11-15 — [paritynov2017postmortem]. Parity's post-mortem of the November library-destruction freeze.
  • Breidenbach, L.; Daian, P.; et al. An In-Depth Look at the Parity Multisig Bug. Hacking Distributed, 2017-07-19 — [paritytechnical2017]. Canonical contemporaneous technical breakdown of the July incident.
  • Mueller, B. What Killed the Parity Multisig Wallet & How to Detect Similar Bugs. HackerNoon, 2017-11 — [paritytechnical2017nov]. Technical breakdown of the November freeze; cited reference for the initWallet reachability on the un-initialised library.
  • OpenZeppelin. The Parity Wallet Hack Explained. OpenZeppelin blog, 2017-07 — [paritytechnical2017oz]. Defender-perspective walkthrough of the July incident and the missing initialisation guard.
  • bokkypoobah. ParityMultisigRecoveryReconciliation. GitHub, 2017 — [paritywhg2017]. Public reconciliation of the White Hat Group's July rescue sweep, including the per-wallet ETH and token amounts moved.
  • Atzei, N.; Bartoletti, M.; Cimoli, T. A Survey of Attacks on Ethereum Smart Contracts. POST 2017 — [atzei2017survey]. Academic taxonomy; places the Parity 2017 incidents within the broader Ethereum attack-class survey.
  • Zhou, L. et al. SoK: Decentralized Finance (DeFi) Attacks. IEEE S&P 2023 — [zhou2023sok]. Modern academic taxonomy; references Parity 2017 as a foundational access-control case.

Discussion

The 2017 Parity incidents have a singular role in EVM smart-contract security history. Together with The DAO 2016, they form the small set of cases that defined how the industry now thinks about pre-deployment controls: The DAO produced the checks-effects-interactions discipline and ReentrancyGuard; Parity produced the initialisation-at-deployment discipline and the proxy-pattern design rules that later became OpenZeppelin's Initializable, the EIP-1967 storage-slot conventions, and the transparent / UUPS proxy variants. Every modern upgradeable contract pattern carries this lineage in its design.

The November 2017 freeze in particular is the historical reason the Solidity ecosystem has been progressively de-emphasising selfdestruct. EIP-6049 (2023) formalised the deprecation; the Cancun upgrade (2024) modified SELFDESTRUCT semantics so that, in most cases, contract code is no longer destroyed even when the opcode is executed. These are not abstract design improvements — they are answers to the question Parity 2017 forced the ecosystem to ask. Contributors writing future Technique or worked-example pages that involve selfdestruct should treat the November Parity freeze as the historical anchor for why the opcode is treated with current-era suspicion.

There is a separate and continuing governance lineage. The proposals raised in 2017–2018 to recover the frozen funds (EIP-156, EIP-867, EIP-999) were each rejected on community consensus that state edits to recover individually-locked funds set an unsustainable precedent — the same "code is law" position that produced Ethereum Classic in 2016, but applied to a freeze rather than an extraction. The Parity-frozen ETH therefore remains as a kind of permanent monument on the Ethereum chain: a structural reminder that the ecosystem has, in practice, decided that the bar for state-edit recovery is the bar set by The DAO, and that no subsequent loss — including Parity 2017 and including any of the larger losses since — has cleared it.

From a defender's perspective in 2026, the 2017 Parity incidents are reproducible in test suites in seconds; any 2020-era static analyser flags the missing initialisation guard immediately. The cost-of-catching-it asymmetry that The DAO illustrates for reentrancy holds equally for Parity's access-control pattern: the bug would have cost low thousands of USD to catch in audit, and cost approximately $330M (~$2.3B+ at 2024 prices) to not catch in production. This is the canonical illustration, on the access-control side, of why the smart-contract security industry is structured around pre-deployment controls — and why OAK-T9.004's mitigation surface is overwhelmingly upstream of runtime detection.

Techniques demonstrated (1)