Solidity Security By Example #08: Unexpected Ether With Forcibly Sending Ether

Phuwanai Thummavet
Valix Consulting
Published in
5 min readJan 25, 2023

By Phuwanai Thummavet

Smart contract security is one of the biggest impediments to the mass adoption of the blockchain. For this reason, we are proud to present this series of articles regarding Solidity smart contract security to educate and improve the knowledge in this domain to the public.

Forcibly sending ether is an attacker’s technique to manipulate a target contract balance. This article will describe how a smart contract relying on improper balance checking can be attacked and how to avoid the issue. Enjoy reading. 😊

You can find all related source code at 👉 https://github.com/serial-coder/solidity-security-by-example/tree/main/08_unexpected_ether_ with_forcibly_sending_ether.

Disclaimer:

The smart contracts in this article are used to demonstrate vulnerability issues only. Some contracts are vulnerable, some are simplified for minimal, some contain malicious code. Hence, do not use the source code in this article in your production.

Nonetheless, feel free to contact Valix Consulting for your smart contract consulting and auditing services.🕵

Table of Contents

  • The Vulnerability
  • The Attack
  • The Solution
  • Summary

The Vulnerability

The following code exhibits the InsecureMoonToken contract. The MOON is a non-divisible token with zero token decimals (line 12). Users can buy, sell, or transfer 1, 2, 3, or 46 MOONs but not 33.5 MOONs.

Besides the non-divisible characteristic, the MOON token is also a stablecoin pegged with the price of the ETH token (line 6). In other words, 1 MOON will always be worth 1 ETH.

Assuredly, the InsecureMoonToken contract is vulnerable. Can you catch up on the issue? 👀

In the InsecureMoonToken contract, users can buy MOON tokens with the corresponding number of Ethers via the buy function (lines 16–24). Users can also sell their MOONs through the sell function (lines 26–36), transfer their MOONs via the transfer function (lines 38–44), get their balances by consulting the getUserBalance function (lines 50–52), and get the total number of Ethers locked in the contract by way of the getEtherBalance function (lines 46–48).

As you can see, the InsecureMoonToken contract is straightforward. However, the contract got an improper balance assertion issue in line 35 in the sell function.

Specifically, the sell function hires the assert(getEtherBalance() == totalSupply * TOKEN_PRICE); statement to strictly assert that the Ether balance of the InsecureMoonToken contract (i.e., the getEtherBalance() part) must always be equal to the total supply of the MOON token (i.e., the totalSupply * TOKEN_PRICE part). This assertion ensures that the number of locked Ethers balances the MOON total supply.

Nevertheless, relying on the contract’s Ether balance as the sell function did is prone to attack. Consider if an attacker can send some small Ethers to lock into the InsecureMoonToken contract. What would happen? 🤔

Gotcha! 😱 The assertion statement would always be evaluated as false because the contract’s Ether balance would no longer match the MOON token’s total supply. This results in reverting all sell transactions.

Since the InsecureMoonToken contract does not implement the receive or fallback function, the contract regularly cannot receive any Ethers. But how can the attacker send Ethers into the contract? Figure 1 below illustrates the solution the attacker adopts to achieve the exploitation.

Figure 1. How the attacker forcibly sends Ethers into the InsecureMoonToken contract

In Solidity, a special function named selfdestruct is used for removing the bytecode from the contract address executing it. Besides the contract bytecode removal, one side effect is that the Ethers stored in the removing contract would be forcibly sent to any specified address.

The selfdestruct function can forcibly send Ethers to even the contract that does not implement the receive or fallback function like the InsecureMoonToken contract. 🤢

This way, if the attacker deploys and executes the Attack contract containing the selfdestruct function, they can forcibly send Ethers to the InsecureMoonToken contract by specifying the InsecureMoonToken contract address as the argument of the selfdestruct function (i.e., selfdestruct(InsecureMoonToken)). 😬

The Attack

The following code presents the Attack contract that can be used to exploit the InsecureMoonToken contract.

To attack the InsecureMoonToken, an attacker performs the attack steps as follows.

  1. Deploy the Attack contract as well as specifying the InsecureMoonToken contract address as the contract deployment argument (line 6)
  2. Invoke the Attack.attack() function along with supplying some Ethers for attacking

After step 2, the supplied Ethers would be forcibly sent into the InsecureMoonToken contract by way of the selfdestruct function (line 14). Then, any sell transactions would be reverted, leading to a denial-of-service attack to the InsecureMoonToken contract. ☠️

Figure 2. The attack result

Figure 2 displays the result of the attack. As you can see, two users bought 55 MOONs with 55 Ethers. But, after the attacker forcibly sent 1 Wei to the InsecureMoonToken contract, the users were no longer selling their MOONs.

Surprise!! You can buy it but may not sell it. 😭

The Solution

The FixedMoonToken contract below is the remediated version of the InsecureMoonToken contract. 👨‍🔧

The smart contract should avoid being dependent on the contract’s Ether balance (i.e., address(this).balance) as it can be artificially manipulated. If necessary, however, the contract should be prepared for such cases of contract balance manipulation.

To remediate the improper balance assertion issue, the FixedMoonToken contract’s assertion statement was improved by using the >= instead of the == symbol as follows: assert(getEtherBalance() >= totalSupply * TOKEN_PRICE); (line 37).

As a result, even if the contract balance is manipulated, the FixedMoonToken contract’s sell function could still work fine.

Summary

In this article, you have learned that incorrectly depending on improper contract balance assertion can lead to a denial-of-service attack in the smart contract. You have understood how an attacker exploits the vulnerable contract and how to avoid the issue. We hope you find this article useful. Until next time.

Again, you can find all related source code at 👉 https://github.com/serial-coder/solidity-security-by-example/tree/main/08_unexpected_ether_ with_forcibly_sending_ether.

Author Details

Phuwanai Thummavet (serial-coder), Lead Blockchain Security Auditor and Consultant | Blockchain Architect and Developer.

See the author’s profile.

About Valix Consulting

Valix Consulting is a blockchain and smart contract security firm offering a wide range of cybersecurity consulting services. Our specialists, combined with technical expertise, industry knowledge, and support staff, strive to deliver consistently superior quality services.

For any business inquiries, please contact us via Twitter, Facebook, or info@valix.io.

--

--

Phuwanai Thummavet
Valix Consulting

Blockchain | Coding | Hacking — I’m a full-time bug collector. Most of the time I bring some bugs to life. Visit my website: www.serial-coder.com