Security Alert: Ethereum Multisign wallet

0

Severity: Critical

Product affected: Parity Wallet (multi-sig wallets)

Summary: A vulnerability in the Parity Wallet library contract of the standard multi-sig contract has been found.

Affected users: Users with assets in a multi-sig wallet created in Parity Wallet that was deployed after 20th July.

Following the fix for the original multi-sig issue that had been exploited on 19th of July (function visibility), a new version of the Parity Wallet library contract was deployed on 20th of July. However that code still contained another issue – it was possible to turn the Parity Wallet library contract into a regular multi-sig wallet and become an owner of it by calling the initWallet function. It would seem that issue was triggered accidentally 6th Nov 2017 02:33:47 PM +UTC and subsequently a user suicided the library-turned-into-wallet, wiping out the library code which in turn rendered all multi-sig contracts unusable since their logic (any state-modifying function) was inside the library.

All dependent multi-sig wallets that were deployed after 20th July functionally now look as follows:

contract Wallet {
    function () payable {
          Deposit(...)
    }
}

This means that currently no funds can be moved out of the multi-sig wallets.

 

Last hack from July 2017 explained:

The attacker sent two transactions to each of the affected contracts: the first to obtain exclusive ownership of the MultiSig, and the second to move all of its funds.

We can see that the first transaction is a call to initWallet (line 216 of WalletLibrary):

// constructor - just pass on the owner array to the multiowned and  // the limit to daylimit  
function initWallet(address[] _owners, uint _required, uint _daylimit) {    
  initDaylimit(_daylimit);    
  initMultiowned(_owners, _required);  
}

This function was probably created as a way to extract the wallet’s constructor logic into a separate library. This uses a similar idea to the proxy libraries pattern we talked about in the past. The wallet contract forwards all unmatched function calls to the library using delegatecall, in line 424 of Wallet:

function() payable {
  // just being sent some cash?
  if (msg.value > 0)
    Deposit(msg.sender, msg.value);
  else if (msg.data.length > 0)
    _walletLibrary.delegatecall(msg.data);
}

This causes all public functions from the library to be callable by anyone, including initWallet, which can change the contract’s owners. Unfortunately, initWallet has no checks to prevent an attacker from calling it after the contract was initialized. The attacker exploited this and simply changed the contract’s m_owners state variable to a list containing only their address, and requiring just one confirmation to execute any transaction:

Function: initWallet(address[] _owners, uint256 _required, uint256 _daylimit) ***
MethodID: 0xe46dcfeb
[0]:0000000000000000000000000000000000000000000000000000000000000060
[1]:0000000000000000000000000000000000000000000000000000000000000000
[2]:00000000000000000000000000000000000000000000116779808c03e4140000
[3]:0000000000000000000000000000000000000000000000000000000000000001
[4]:000000000000000000000000b3764761e297d6f121e79c32a65829cd1ddb4d32

After that, it was just a matter of invoking the execute function to send all funds to an account controlled by the attacker:

Function: execute(address _to, uint256 _value, bytes _data) ***
MethodID: 0xb61d27f6
[0]:000000000000000000000000b3764761e297d6f121e79c32a65829cd1ddb4d32
[1]:00000000000000000000000000000000000000000000116779808c03e4140000
[2]:0000000000000000000000000000000000000000000000000000000000000060
[3]:0000000000000000000000000000000000000000000000000000000000000000
[4]:0000000000000000000000000000000000000000000000000000000000000000

This execution was automatically authorized, since the attacker was then the only owner of the multisig, effectively draining the contract of all its funds.

This makes you wonder, how safe is Parity Multisign wallet? At this moment all multisignature Ethereum wallets are frozen.