anyone can kill your contract

A forensic examination of the Parity multisig wallet exploit.

Firstly:

Loosing money (or worse getting it stolen) is not a pleasant experience, this article is my attempt to make sense of what happened to round up my understanding of solidity and smart contracts and doing forensic analysis when things go wrong.

Reference:

Coindesk post ( a good overview so far): https://www.coindesk.com/ethereum-dao-fears-forks-finger-pointing-parity-exploit-aftermath/

Github Issue: https://github.com/paritytech/parity/issues/6995

Parity Post: https://paritytech.io/blog/security-alert.html

I've tried keeping this discussion accessible, but if you can't make sense of some of it ...
I documented my learning curve with smart contracts and turned it into a set of notes here on medium ( and maybe a forthcoming ebook ): 
Part 1. Setting up.
Part 2. Web3.js/node.
Part 3. Solidity.
Part 4. Smart Contracts.
Part 5. Smarter Contracts.
Part 6. Tokens & Inheritance.
Part 7. ERC20 Token Standard.
Part 8. Crowdfunding and ICOs.
Part 9. Dapps & MetaMask.
Part 10. Remix, Truffle, TestRPC.
Part 11. Some intermediate considerations.
Part 12. Some advanced considerations.

Background:

Parity is a client for the Ethereum Blockchain, it can be used as a wallet or an interface for Ethereum smart contracts. ( see the note on setting up for an overview), this exploit ( as well as a previous one ) only affects multisig wallets in Parity and not regular accounts.

What’s a multisig wallet ?

Good question, let’s make one ( on the Kovan test network ):

So Parity’s multisig Wallet is a smart contract which provides the functionality of multiple ownership ( owners need to sign transactions ) and wallet day limits. It should be noted that in some sense they are a second class citizens ( a smart contract ) to normal accounts ( first class citizens ), which even before getting into the exploit should raise some flags to experienced blockchain users, mainly a 3rd party risk is introduced in the form of the smart contract and thus trust is delegated to the writers of the contract, although in Ethereum anyone can review or audit the contract which translates into users responsibility ( we will get to that soon ).

So first, let’s fund our multisig wallet, and then transfer some ether out.

I can’t help but notice that the wallet transfers Ether without 2 confirmations ( apparently because both accounts share the same coinbase, it’s a feature not a bug ?). In any case, it does require multiple signatures when modifying the contract by adding owners ( although I ran out of gas so I couldn’t update the daily limit or required owners, but you get the idea) :

I should say that interacting with the multisig wallet is not very straightforward or intuitive due to a combination of UI issues and lack of clarity on what it does,in short it is a complex undocumented piece of software…another flag when considering millions of dollars were deposited in them.

Recreating the Exploit

So now that we have a bit of background and know in general what a multisig wallet in parity is, let’s try recreating the exploit, for that we will need more details, luckily the Ethereum ecosystem (as well as the github issue) provide a lot of information…

The contract: https://github.com/paritytech/parity/blob/6b0e4f9098be6b841353e7c4f116aa86b7c2e3d6/js/src/contracts/snippets/enhanced-wallet.sol

Contracts address: https://etherscan.io/address/0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4

The Exploit :

Calling the initWallet function on the uninitialized multisig wallet library contract would give ownership and control to the sender, in this case a suicide method would render the library and the subscribing contracts incapable of receiving or sending Ether, the funds then become frozen. It should be noted that the suicide (or self-destruct) method ( also see can you kill a contract in my notes part 11 ) as originally intended takes one argument in the form of an address, to which the Ether balance is then sent, something that apparently didn’t happen, but it should be noted that an attempt to change ownership from dependent contract and retrieve ether was performed soon after by the same address.

Other takes on it… https://ethereum.stackexchange.com/questions/30128/explanation-of-parity-library-suicide

Recreating it :

The plan is to release a new multisig wallet contract on the testnet, and then try recreating the exploit.

 This is a good way to test and learn ( on a testnet) but for whatever reason the exploit happened on the live main net. 

Recreating the past proves challenging…

Before deploying in my compiler I needed to downgrade solc to a compatible version...
$ sudo npm uninstall solc
$ npm view solc versions
$ sudo npm install solc@0.4.9

That doesn't work, and remix will not deploy… need to find out more about the original contract deployment; if you are to believe one of the original developers it was a bit rushed ( source hidden cause I am not here to prosecute anybody) :

The bug was easy to fix and the new contract was reviewed by 7 developers on Github. It’s not a code issue, it’s an issue in the rushed deployment of the new code we forgot to call initWallet at least once to claim the library. But we had to hurry because there was the chance people will deploy new wallets with the bug ( note: referring presumably to the previous vulnerability in july ).

So the alternative is to grab the contract creation data and deploy it as a transaction to an address, this will hopefully emulate the original deployment…

// In Web3 nodejs :
web3.eth.sendTransaction({
data: '0x' + contractCode.code,
from: web3.eth.coinbase,
gasPrice: gasPrice + 1,
gas: gasEstimate + 1
}
// contracCode.code is the contract creation code found on etherscan.

And voila: A clone replica of the affected library contract:
https://kovan.etherscan.io/address/0x5a397993b8d2fd93d1e1edc2dd7f21167e5396d4#code ( if it's already suicided you'll have to make another one to test ).

More Questions…

So this is a library, but how does Parity make new multisig wallets with it ?

Multisig wallets in Parity are linked to the existing contract upon deployments, they basically have a field _____________WalletLibrary______________ and the address of the deployed library is attached at creation time, presumably linking them forever. Linking can be done via the compiler or in our case by simply replacing the contract library constant:

address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;

With the already deployed library:

0x5A397993B8D2fd93D1E1edC2dD7f21167e5396d4

This is apparently done ( not tested ) to save cost on deployment vs an individual multisig instance with all the library functions.

Parity obviously has a nicer UI,but you could make a Dapp that makes multisig wallets relatively easily, in our case we’ll deploy a multisig wallet through remix ( see this post for specific instructions ), and then add it as a wallet in parity…

To confirm it is working, we’ll fund and withdraw from it through parity (I also increased the daily limit to 5 ETH), the address in case you want to see the history is : 0x60366a1c727ea53D662A733Fa9758d1FeD5C4187

Now to test the exploit ( step by step) :

1. Call initWallet method from any account, this will give anyone calling ownership of the library...
// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit
function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
initDaylimit(_daylimit);
initMultiowned(_owners, _required);
}
2.Call The kill method ( which suicides the contract):
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}
3. That's it, now no funds should be able to get out of the MultiSig wallet previously created...
The effects also includes the inability to read the contract:
https://kovan.etherscan.io/address/0x5a397993b8d2fd93d1e1edc2dd7f21167e5396d4#code
As well as the side effect of any multisig wallet linked to the library now behaving like a black hole contract;
contract Wallet {
function () payable {
Deposit(...)
}
}
You can send ether but it won't come out (not tested). 

Findings:

While this has been mostly an exercise in learning how to recreate the past and a significant event in the Ethereum blockchain, I would also like to mention some findings that might help others and myself moving forward, once again just my opinion, yours might differ mine might evolve.

1.This was a preventable event and a simple (but costly) oversight:

The root cause of this event was the oversight of not calling the initWallet method of the library contract and claiming ownership in the moments after deploying it, for whatever reason the team or developer failed to do so. Human errors of these types have always been part of business and human activities, the causes could be multiple: pressure to deploy, team structure & culture and lack of checks & balances to name a few. A simple preflight checklist could have avoided this oversight, but as mentioned the environment & culture also needs to be there.

2.MultiSig wallet & smart contracts are complex,maybe too complex for their current use.

A multisig wallet ( especially one that uses the library pattern) is a complex smart contract, in itself a complex piece of software. As such it needs to be better documented and tested, unit testing and inline comments might not be enough,3rd party audits from multiple sources could be helpful as well as a more down to earth explanation of what it does and the risks involved in using it. While anyone can read deployed smart contracts code,not everyone can be expected to know the ins and outs of the language…There is also a risk/reward benefit relationship and/or technical debt associated with both the pattern used and multisig wallets. Sure, they are safer, but at what cost, sure, they are convenient and easy to deploy, but at what cost, sure the pattern saves money, but at what cost ?

3.There are multiple chasms in between Developers,Tooling, Blockchain Clients and Consumers which contribute to these type of mistakes.

This might sound counterintuitive or preachy, but users also have a responsibility here; there is little oversight of blockchain projects and maybe there will never be since they are decentralized, so the responsability lies with users to understand the risks. I think greed and novelty could be a blinding factor to these risks.

Blockchain clients like mist,parity and metamask are works in progress and are continually changing, some parts of them like multisig wallets are a 3rd party risk, but since they are bundled together they appear to have the same security features and behavior as the underlying currency, this point needs to be explained better.

Tooling is fractioned, poorly documented and complex ( Mostly due to the economics involved and the newness of it all). While better tooling is unfortunately always a luxury, it can also allow for better tests,code and forensics.

Developers are also a contributing factor. It’s quickly being realized that coding mistakes, complex code and other minor issues in other languages have heavy consequences here since real money is at play. Rather than becoming more accountable, I think developers should acknowledge that they need to dilute this responsability, companies should also take this into account, so for instance, rather than think that the job is done when a smart contract is written and posted on a github repo, there should be considerable additional work done ( documentation, testing, multiple audits) before it’s really done.

4. Whose gonna pay for all these findings?

Perhaps more profoundly there lies the finding that blockchain companies & projects ( like most tech companies & projects) are under a lot of pressure to perform, so while resources might be scarce there is an incentive to over promise and over reach with the consequence of under delivering and making mistakes along the way, this I think is unfortunately part of the economic system in which we live…

5. What about the exploiter.

I don’t think you are legally obligated to test others code in a safe test environment and then report the findings, perhaps there is a moral obligation, but that’s harder to define. There might be responsibility for exploiting a public facing bug, but some malicious or self serving intent is probably needed, in this particular case it is not as clear cut as in previous exploits.

Remaining questions:

I would love to figure these out :

  1. What happens now ?

Estimates for the funds frozen range between 150 – 280 million USD, or around 500k- 900k ETH. ( not confirmed ) This issue could be resolved and funds made available again via a hardfork that would somehow modify certain relevant transactions and maybe the multisig contract ( subject to miners approval and a proposal ). This could be considered a heavy handed approach by some ( the rules change when it’s convenient ) and create a split (another one). On the opposite side, not doing anything would leave folks without their funds and maybe legal action could be initiated in the form of a class action lawsuit against those involved. Whatever the outcome ( these are just speculations), a precedent is being formed.

2. How did he got the address ?

One last thing that I couldn’t quite figure out is how did the exploiter figure out the multisig library address, it has been suggested it was picked out of his own multisig wallet ( by analysis of the bytecode) although I couldn’t recreate it. Was it published by Parity ? I also couldn’t figure out how it was included in Paritys UI call. This is probably a minor detail, but would be nice to know.


I hope this short exploration was helpful to those trying to make sense of what happened (especially those at a loss ) and those curious about performing forensic analysis of blockchain events & smart contracts in Ethereum as well as testing future bugs and exploits in a non harmful and safe way.

Psst !  
If you are looking for an introduction to Ethereum, Solidity and Smart Contracts, I wrote a small book that is designed to get you up to speed in no time.
Available in ebook and paperback:
https://www.amazon.com/dp/B078CQ8L7V
     🙏 🙏 😊 🙏 🙏

Thanks for reading !

Keno

About the Author :

Born Eugenio Noyola Leon (Keno) I am a Designer,Web Developer/programmer, Artist and Inventor, currently living in Mexico City, you can find me at www.k3no.com