• YouTube Channel
  • System Status
  • VS Code Extension
  • Unsafe Self-destruct

    Overview

    What is the Unsafe Self-destruct vulnerability?

    The Ethereum Virtual Machine (EVM) has included instructions to destroy deployed contracts, with associated functionality in the Solidity language. The deprecated suicide() and current selfdestruct() functions in the Solidity language will destroy a deployed contract and send any ether it controls to a specified address. Any functionality offered by the contract will no longer be available to users and other smart contracts on Ethereum.

    If this capability is included in a smart contract but is not sufficiently protected, a contract may be destroyed inadvertently (or maliciously), subverting the original author's intent.

    A real-world example: the Parity multisignature wallet

    Also described from the perspective of the Locked Ether vulnerability, in 2017 the Parity multisignature wallet smart contract system was exploited when a user took control of a library contract that offered core functionality to several hundred multisignature wallet contracts. This user then took advantage of the library contract's self-destruct capability which was inadvertently left accessible by the developers in this scenario. The destruction of the library contract caused the dependent multisignature wallet contracts to cease functioning properly, leading to the locked ether vulnerability as discussed.

    Further reading: A Postmortem on the Parity Multi-Sig Library Self-Destruct

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity 0.8.0;
    
      contract Vulnerable {
        // core smart contract functionality elided
    
        function destroyContract() public {
          selfdestruct(msg.sender);
        }
      }
    
    

    In the above example, the contract Vulnerable exposes a public function that allows any user to destroy the contract and send any ether it holds to the caller's address. This function is not protected by a require() statement or other mechanism to ensure that only the contract owner can call it, so any user can destroy the contract and take its ether.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity 0.8.0;
    
      contract ProtectedSelfDestruct {
        address owner = msg.sender;
    
        // core smart contract functionality elided
    
        function protectedSelfDestruct() public {
          checkBeforeDestroy();
        }
    
        function checkBeforeDestroy() private {
          // alternative (or multiple) conditions could be checked here, beyond ownership
          require(msg.sender == owner);
          selfdestruct(payable(msg.sender));
        }
      }
    
    

    In the corrected example, the call to selfdestruct() is contained in a private function, which also checks that the caller is the owner of the contract. This ensures that only the owner can destroy the contract and take its ether, in this case. Additional logic could ensure that other conditions are satisfied before allowing the contract to be destroyed, such as registration of an alternative destination contract in a forwarding pattern that previously registered this contract.