This blog post provides an update on findings following the discovery of a storage corruption bug last week. In summary, the bug was much less severe than initially thought. The small number of affected contracts we discovered may only be exploitable by the owner, or the exploit may only break the user interface and not the actual contract logic. All of the exploitable contracts/dapps we have reviewed can be modified without upgrading the contract itself. Of course, you should still check the contract for safety.
After discovering a storage corruption bug in the Solidity compiler and recognizing that it could have a serious impact on already deployed contracts that cannot be updated, we began analyzing how common the bug was and how we could fix exploitable contracts. .
We focused on contracts with source code published on Etherscan. This is because important or popular smart contracts usually publish their source code there to gain trust from users and allow them to verify compilation. Moreover, if the source code is not available, it is much more difficult for an attacker to find a suitable attack method. Finally, contracts that are used privately (and therefore do not require source code publishing) typically ensure that the contract is called at a specific address, so an attacker has no means of writing to the repository.
To automate the process of verifying all contracts in Etherscan, we created a modified version of the Solidity compiler that can automatically detect conditions that cause bugs. The technology has already reduced the number of potentially vulnerable contracts to 167. We then manually checked for potential storage corruptions that would make those contracts vulnerable to attack.
Only 10 contracts were found to be vulnerable, and we were able to contact most contract owners/developers. Of those contracts, 7 out of 10 can only be exploited by their owners in that they can change certain parameters outside of the permitted range or unlock previously locked contracts. One contract can be exploited by unauthorized users, but it has another major flaw in its design. The other two contracts found to be exploitable by unauthorized users either provided no benefit when exploited or only affected the user interface.
Why are there so few exploitable contracts?
First, let’s define what “exploitable” means.
Storage corruption bugs can be exploited if they can be used to modify variables in storage in a way that would not be possible without the bug, and these modifications affect the behavior and use of smart contracts. For example, a contract is not considered to be exploitable in the following situations:
- The same account can overwrite variables in the same contract state in the usual way.
- Overwriting is only possible at creation time. (Note that I did not check whether an overwrite occurred at that time.)
- Overwrites are only triggered in unexpected situations where the contract logic has been corrupted anyway (e.g. a 32-bit counter incremented once per block, overflow).
- It can overwrite variables that may seem unused and unimportant in a smart contract, but may be part of the public interface.
Why is this critical bug exploitable in only a very small number of cases?
It is a combination of the following factors that both significantly increase and significantly reduce the potential for exploitation:
- Smaller types are rarely used as they only provide benefit in very rare cases.
- Small types must be adjacent to each other in the repository. Having one big type in between prevents bugs from occurring.
- State variables are often assigned one after the other, eliminating corruption in the second assignment.
- In the remaining cases, the most common combination of “address” and “bool” is used, but here the address variable is often assigned “owner”. message.sender Therefore, it cannot be exploited. Even if the owner can change, a flag is often a flag that the owner can still set through other means.
How to fix affected contracts
The vast majority of exploitable contracts can only be exploited by the contract owner, administrator, or developer, especially with a single feature that allows for owner changes. This attack can further elevate the owner’s privileges. To prevent owners from taking advantage of this exploit, a proxy agreement can be installed between the owner and the affected contract. This proxy contract forwards calls from the owner but does not allow exploitable function calls. If you need to call an exploitable function, a proxy contract can prevent malicious data from being passed into the contract.
If you have specific questions or concerns regarding the Agreement, please contact us at: grid.
Legal Notice
The comments in this post are recommendations for resolving storage corruption bugs in the Solidity compiler. As you know, we work in an emerging and evolving field of technology. The same things that make this work exciting—innovation, influence, and increased understanding of contracting capabilities—are the same things that make it risky. If you decide to implement the recommendations in this post and continue to participate, you should check how they affect your specific contract and understand that there are risks involved. If you choose to implement these recommendations, you solely assume the risk of the results.