The Smart Contract Weakness Classification and Test Cases (SWC) Registry is a set of Web3 vulnerabilities to avoid when writing smart contracts. It may seem daunting to understand every issue so I’ll do my best to demystify every issue and explain each vulnerability with real-world examples. This write-up is intended for those who are just starting out in solidity development and smart contract auditing.
SWC-106: Unprotected SELFDESTRUCT Instruction
SELFDESTRUCT is a keyword that is used to terminate a contract, remove the bytecode from the Ethereum blockchain, and send any contract funds to a specified address.
There are two vulnerabilities pertaining to the SELFDESTRUCT opcode. Firstly, it can delete a contract. Secondly, it can transfer unwarranted Ether into a contract. In this write-up, we will explore these two problems in detail and have a better understanding of how to use this dangerous opcode and how to prevent it from harming our contracts (PS. Although some articles mention that including a selfdestruct function in your contract is a sensible security practice as it safeguards contract funds against malicious attacks, it is better to not include this functionality inside your contracts unless you absolutely know what you are doing)
How does SELFDESTRUCT work
Advantages and Disadvantages of using SELFDESTRUCT
How can SELFDESTRUCT affect another contract?
Notes
Summary
References
How does SELFDESTRUCT work
Above is a simple example of selfdestruct. The function sudicideAnyone() is a public function (by default if no visibility modifier is explicitly stated, SWC-100), which means that anyone can call this function. Inside the function, the selfdestruct keyword is called, and msg.sender is passed in as a variable. This means that any ether in the contract will be transferred to the msg.sender, and the contract will be deleted from the blockchain. Ideally, funds should be sent to a contract/address that is able to receive ether, so the payable function is used. Remember, a payable address can receive Ether, while a plain contract address cannot.
Advantages and Disadvantages of using SELFDESTRUCT
When developers intend to change to a new contract instead of upgrading the current contract, the selfdestruct keyword is a convenient way to transfer any ETH inside the old contract to the new one. Also, if there is an ongoing security breach in the contract, the developer can also call selfdestruct to immediately transfer the rest of the funds to another contract to minimize losses.
However, selfdestruct has its own flaws. For example, if the developers and the users do not communicate with each other, the users may send ETH to the old contract not knowing that it has already been destroyed, resulting in funds loss. Also, the developers may use this keyword as an easy way out to rug the protocol. Lastly, selfdestruct can only transfer Ether and not other ERC-20 tokens. If the contract also holds other tokens, then the assets can never be recovered.
Always make sure that the selfdestruct function has the appropriate access controls in order to prevent malicious parties from self-destructing the contract. Also, consider removing the self-destruct functionality unless absolutely required. If there is a valid use case, it is recommended to implement a multi-sig scheme so that multiple parties must approve the self-destruct action.
How can SELFDESTRUCT affect another contract?
Let’s take a look at this Ether contract.
The goal of this game is to be the 7th player to deposit 1 Ether.
Players can deposit only 1 Ether at a time.
The winner will be able to withdraw all Ether.
What is the issue with this contract?
That’s right! The winner is only crowned when the balance == targetAmount. The targetAmount is set at the start of the contract, which is 7 ETH. If, by some magical way, ETH is deposited into the contract, then the balance amount will never be equal to targetAmount.
An attacker can grieve the contract by selfdestructing a contract with some ETH inside and passing the amount into this EtherGame contract! Here is what the attack contract looks like:
The EtherGame contract will be useless now because no one can be the winner.
Notes
The SELFDESTRUCT function does not remove the history of a contract from the Ethereum chain — it only removes the contract's bytecode.
The SELFDESTRUCT opcode most likely going to be deleted in an upcoming fork (https://eips.ethereum.org/EIPS/eip-4758), which would break contracts/protocols relying on it when that happens.
Summary
SELFDESTRUCT will destroy a contract
Only ETH value will be passed over to the new contract address
Try not to use SELFDESTRUCT unless absolutely required
References
https://swcregistry.io/docs/SWC-106
https://www.alchemy.com/overviews/selfdestruct-solidity
https://eips.ethereum.org/EIPS/eip-4758
https://ethereum.stackexchange.com/questions/144210/selfdestruct-deprecated-in-solidity-0-8-18
https://solidity-by-example.org/hacks/self-destruct/