Postmortem: The what, when, and how of the Nov 6th Parity Wallet Hack behold my photoshop skills TL;DR The WalletLibrary contract deployed 1 day after the first Parity Wallet Hack contained a bug that allowed anyone to execute . Someone experimenting with the previous exploit called and, subsequently, on the WalletLibrary, removing it from the . Because the actual wallet contract delegates all calls to this hard-coded WalletLibrary contract, it no longer has the logic to send funds. Parity Multisig initWallet initWallet kill blockchain Approximately 513k ETH (≈154 million USD) has been locked in the affected contracts. No funds were “stolen” per-say; only made unreachable. The user (whose name will go down in history 😄) did this accidentally while experimenting with the previous well-known exploit. devops199 What’s Different from Parity Wallet Hack 1? I’d recommend reading this technical breakdown on how the first hack went down. _A vulnerability was found on the Parity Multisig Wallet, that allowed a hacker to steal over 150,000 ETH._blog.zeppelin.solutions The Parity Wallet Hack Explained The simplified version of Parity Wallet Hack 1 is as follows: The contract is a simple contract that uses to execute transactions using ’s code The WalletLibrary contract assumes that it will be called in the context of a contract that have state that it can modify (namely ) and this is the core assumption that caused the recent disaster. In the first Parity Wallet Hack, the hacker changed the state of various contracts by delegating a call to , setting themselves as the owner of the contract and then withdrawing funds normally. Wallet delegatecall WalletLibrary within the context of the Wallet function. does m_numOwners Wallet initWallet Wallet How and Why In this case, the internal state of the contracts that have been deployed hasn’t been changed; what changed was the internal state of the contract. The contract contains a state variable (along with and similar) that it expects to be shadowed by the calling contract’s own state. The rest of its state is globally shared between all Parity Multisig Wallets that hardcode its address. Wallet WalletLibrary WalletLibrary m_numOwners m_owners After deploy, the WalletLibrary contract is simply uninitialized. is . This means that the modifier (which would act correctly when called in the context of an initialized Wallet contract) always passes. If the WalletLibrary isn’t executed in a Wallet contract’s context, is 0, allowing anyone to call methods that this modifier guards, one of which is . m_numOwners 0 only_uninitialized m_numOwners initWallet Anyone could have called this function at any time in the past 110 days since it was deployed, but it obviously went undetected until now. Result (the Polkadot ICO wallet, containing ~90m USD in ether). Note line 451 where the WalletLibrary address is hard coded. Assuming all calls to this address fail (comments removed and knowing that the constructor isn’t actually included in the contract bytecode), this is what the contract looks like right now: Here’s an example of an affected wallet contract Wallet is WalletEvents {function() payable {if (msg.value > 0)Deposit(msg.sender, msg.value);} function getOwner(uint ownerIndex) constant returns (address) {return address(m_owners[ownerIndex + 1]);} address constant _walletLibrary = 0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4; uint public m_required;uint public m_numOwners;uint public m_dailyLimit;uint public m_spentToday;uint public m_lastDay;uint[256] m_owners; … which doesn’t do much at all, and certainly doesn’t send funds anywhere. Timeline T= Jul 20, 2017 04:39:46 PM UTC The following the aftermath of Parity Hack 1. . Of note are lines 215 ( ) and 225 ( ). [WalletLibrary](https://etherscan.io/address/0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4) contract was deployed You can see the verified source code on Etherscan only_uninitialized kill T= Nov 06, 2017 02:33:47 PM UTC was sent to WalletLibrary, calling the method. This transaction succeeded in making the sole owner. Transaction 0x05f71e1b initWallet [0xae7168deb525862f4fee37d987a971b385b96952](https://etherscan.io/address/0xae7168deb525862f4fee37d987a971b385b96952) https://etherscan.io/tx/0x05f71e1b2cb4f03e547739db15d080fd30c989eda04d37ce6264c5686e0722c9 T= Nov 06, 2017 03:25:21 PM UTC (+51 minutes from previous tx) was sent to WalletLibrary, calling the method with as the beneficiary address. Transaction 0x47f7cff7 kill [0xae7168deb525862f4fee37d987a971b385b96952](https://etherscan.io/address/0xae7168deb525862f4fee37d987a971b385b96952) https://etherscan.io/tx/0x47f7cff7a5e671884629c93b368cb18f58a993f4b19c2a53a8662e3f1482f690 T= Nov 06, 2017 03:54:34 PM UTC (+29 minutes from previous) devops199 created documenting the transactions. parity#6995 https://github.com/paritytech/parity/issues/6995 T= Nov 06, 2017 04:33:00 PM UTC (+39 minutes from previous) devops199 posts a link to the issue in the . parity gitter channel I can’t seem to link to gitter messages, so a screenshot will have to do. and is understandably worried about the consequences. T= Nov 6th, 2017 07:51:00 PM UTC (+ 3 hours and 18 minutes) Parity releases a warning and states that they’re investigating. Possible Resolution Avenues Off the top of my head, here are the three possible approaches for recovering funds: Hardfork, ala DAO Hack, which may or may not result in another “classic” fork. If this is done during the planned Constantinople hardfork, it may proceed particularly smoothly. Break cryptography and deploy a new contract to the hardcoded WalletLibrary address. Which is, at the moment, cryptographically infeasible before the heat death of the universe. , which provides a community-controlled way to recover funds during events such as these. While this EIP doesn’t specifically solve the issue ( ), there is at least some precedent for the community supporting a method like this to avoid a hard fork. Implement EIP156 as noted by sciyoshi In general, though, it seems the funds are locked for the foreseeable future. Possible Prevention Measures How could this have been prevented? The primary cause of the bug seems to be twofold: The quickly-patched WalletLibrary code was not audited after the first Parity Wallet Hack. The library pattern hid the fact that WalletLibrary is an actual contract with internal state. This encouraged an assumption that it would only be called in the context of a Wallet contract. The Solidity library construct would have made this bug much more obvious; Solidity library contracts aren’t allowed to have internal state, so the fact that anyone could call would have been much more obvious. Additionally, they wouldn’t be able to implement ownership (internal state), which is what made this code feel safe; the modifier was expected to perform correctly. initWallet onlyOwners Some possible prevention measures (could have been): After deploying the WalletLibrary contract, anyone could have called with a null owner, effectively locking the contract from change (when called directly). It would still function correctly as a library when the Wallet contract state shadows the null-owner state of the WalletLibrary contract. addOwner Placing the logic within a contract that was designed to be immutable is a suspect decision. Again, it seemed safe because of the modifier and Wallet context assumption, which is why wasn’t placed directly on the Wallet contract itself. At least in the future, monitoring lines will be part of every code audit. kill onlyOwners kill selfdestruct In general, the globally shared state of the WalletLibrary contract was a curious decision. It didn’t directly contribute to this hack, but transparently sharing state between MultiSig contracts like this just feels . weird Who’s Affected? Here’s a list of the wallets affected by the deletion of WalletLibrary, ordered by net worth. ! but wait there’s more Of note: the with 306k ETH Polkadot ICO wallet 0x3bf an with 115k ETH ICONOMI-affiliated wallet 0x376 a with 16k ETH MUSICONOMI-affiliated wallet 0xc7c if you know the owners of the other wallets and don’t mind listing them, let me know! Conclusion Nice. The situation is still developing, to an extent. Parity will most likely announce an update . on Twitter To be clear, I place exactly 0 blame on devops199, and I don’t think anyone else should either. ps. I was the first person to think of advertising on the contract’s old address. On that note, if this post made you happy, informed, or some other vaguely positive emotion, please smash that 👏 button because I feed on imaginary internet points. Thanks to Dror Liebenthal for proof reading the post.