22

So today, A vulnerability found within the Parity walletLibrary which resulted in freezings of potentially hundreds of millions of dollars from wallets using parity multi-sig. Can someone technically explain how this was abused?

Here is link to the vulnerable code

2 Answers 2

10

Libraries on Ethereum

First off, there's 3 ways to call a function on a contract. CALL, CALLCODE, and DELEGATECALL.

Libraries on Ethereum are largely implemented with DELEGATECALL. Meaning, you deploy a contract that serves as a library - it's got some functions that anyone can call, and can even change the storage of the calling contract.

Solidity has some syntactic sugar that lets you declare a library, which does all the DELEGATECALLs for you if you use the library in your contract. But on the EVM level it is the same as deploying them as contracts.

Parity's library contract

Parity initially deployed a mutisig contract as their library. The library itself is a very much working multisig wallet. On a hindsight it probably shouldn't be.

Then all the other multisig wallets have this in their code:

address constant _walletLibrary = 0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4

Since it's a constant, it's populated at compile time, meaning it's permanently stored in "code", not "storage". The value would be the address of the library to DELEGATECALL on. If you run eth.getCode(walletAddress) on one of the affected wallets, you should see the address of the now-dead library at index 422.

On another hindsight, it should probably allow users to change the linked library address, instead of coding it in the bytecode.

Killing the library contract

Since the library itself is a working wallet, and it was left uninitialized, anyone would be able to call initWallet on the library contract. The "hacker" did it in this transaction. The call is perfectly legal, zero tricks involved, since he's just initializing a wallet that's not been initialized.

Now that he is the owner of the library contract, he can call any privileged function he wants. So he called kill in this transaction. The kill function basically calls suicide (which is now being replaced by selfdestruct, they do the same thing). suicide destroys the contract, clears its storage and code, sending remaining funds to the owner. (Which is zero, since the library contract holds no funds)

Code becomes zero

Now every function call to the now-dead library will simply return false, or 0.

All the multisig wallet contracts that rely on the library contract code would now get zero when they DELEGATECALL. The multisig wallet contracts still hold funds, they have not been hacked, but all the library code is zeroed out.

The multisig wallets are essentially locked. Most of the functionalities (including the kill-switch function) rely on the library which is now returning zero for every function call.

The only way to resume their functionalities would be a hard fork to add the library code back.


Can you statically link the libraries?

It is very possible to contain all the library code in the multisig wallet contract itself, instead of doing DELEGATECALLs to an external contract. This is similar to static linking in a sense. It will increase the gas cost of contract deployment though.

2
  • This is by far the best non-expert answer. For those of you who still remain clueless to what everyone is talking about, please see my links and comments in the question.
    – not2qubit
    Nov 13, 2017 at 15:51
  • Is there any fix for this or all the money that stored in the library I guess millions of dollars gone? @libertylocked
    – alper
    Sep 20, 2018 at 11:36
22

Parity employed library driven smart contract development for their multi-sig wallets. Meaning, all multi-sig wallets referenced this single library contract for all their functionality.

An unwitting user took control of this library contract by calling the initWallet function on the library contract, which gave him ownership of the library contract as it had not been initialized (since it was a library contract) and the variable only_uninitialized had not been set. UPDATE: This could have been avoided if, after Parity deployed the library contract, it called initWallet once to claim the contract and set the initialized variable. Source

The user then suicided self-destructed it, obliterating functionality for ~500 multi-sig wallets and effectively, irreversibly freezing ~$150M. A hard fork would be required to restore the contract and/or return funds.

As for technical details, all that can really be said is that these multi-sig wallets referenced this single library contract using delegate calls. This is a normal part of smart contract development and functionality.

However, Parity did not anticipate the scenario of this library contract being suicided.

Here is a great article from Aragon on library-driven smart contract development.

Wallets deployed before July 19 used a different library contract with a similar initWallet bug, but the Library contract could not be taken over in a similar fashion as it had already been initialized by the developers at Parity.

Update:

Upon further inspection, it appears the WalletLibrary contract was initialized using contract instead of library, with the actual wallets making simple DELEGATECALL's to this tenuously linked smart contract, instead of using the recommended practice of implementing a library and using it. This makes the library behave more like a Singleton than a Library.

10
  • How do we explain, "An unwitting user took control ..." and "The user then self-destructed ..."? Honest mistakes? Nov 8, 2017 at 4:34
  • 3
    More or less. The problem is that the contract was simply a library with a bunch of functionality and held no Ether, so the self-professed “newbie” saw a 0 balance contract and tried killing it, unwittingly pulling the mother of all (rm -rf /)’s.
    – prototypik
    Nov 8, 2017 at 4:43
  • As an addendum, I'd like to add that some users have called this users motives into question. It does appear that he was trying to kill the library in order to make it impossible for Parity to recover funds as he drained some of the other contracts, as he clearly attempted to gain control of the Polkadot contract immediately after killing the Parity library contract. It wasn't until he realized what he had done that he reported the issue, since at that point no further profit could be made.
    – prototypik
    Nov 8, 2017 at 17:46
  • This is interesting, nice findings valkn0t ;-) Do you guys think this could be an insider job or something? why initiate a library as a contract but declare it as a library this is nonsense to me
    – NowsyMe
    Nov 9, 2017 at 12:45
  • 1
    No. Wallets deployed before July 19 used a different insecure library that was initialized. A user could not have taken control of the library contract in the same manner before July 19.
    – prototypik
    Nov 12, 2017 at 16:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.