Exploring Smart Contract Vulnerabilities: The King of Ether
Today we are going to go deeper into the world of smart contract vulnerabilities, focusing on an interesting weakness which came into prominence because of Ethereum-based auction game. This attack happened because of misallocation of gas. Before we get into the details of the attack, let’s look into the game itself and the concept of gas.
Exploring Smart Contract Vulnerabilities: The King of Ether
So, the “King of the Ether Throne” (KotET) is a game where participants compete with each other for the title of “King of the Ether.” So, how does that work?
By simple bidding. Here is how the bidding process works according to their website:
- Suppose the current claim price for the throne is 10 ether.
- You want to be King/Queen, so you send 10 ether to the contract.
- The contract sends your 10 ether (less a 1% commission) to the previous King/Queen, as a “compensation payment”.
- The contract makes you the new King/Queen of the Ether Throne.
- The new claim price for the throne goes up by 50%, to 15 ether in this case.
- If a usurper comes along who is willing to pay 15 ether, they depose you and become King/Queen, and you receive their payment of 15 ether as your “compensation payment”.
In simple terms, it is a Ponzi Scheme.
Now, before we proceed with what how this ties in with smart contract vulnerabilities, let’s understand how gas works.
What is Ethereum Gas?
“Gas” is the lifeblood of the Ethereum ecosystem, there is no other way of putting that. Gas is a unit that measures the amount of computational effort that it will take to execute certain operations.
Every single operation that takes part in Ethereum, be it a simple transaction, or a smart contract, or even an ICO takes some amount of gas. Gas is what is used to calculate the number of fees that need to be paid to the network in order to execute an operation.
All the smart contracts that run in the EVM are coded using solidity (Ethereum is planning to move on to Vyper from Solidity in the future.) Each and every line of code in solidity requires a certain amount of gas to get computed.
To better understand how gas works in Ethereum, let’s use an analogy. Suppose you are going on a road trip. Before you do so you go through these steps:
- You go to the gas station and specify how much gas you want to fill up in your car.
- You get that gas filled up in your car.
- You pay the gas station the amount of money you owe them for the gas.
Now, let’s draw parallels with Ethereum.
- The car is the operation that you want to execute, like a gas or a smart contract.
- The gas is well….gas.
- The gas station is your miner.
- The money that you paid them is the miner fees.
All the operations that users want to execute in there must provide gas for the following:
- To cover its data aka intrinsic gas.
- To cover its entire computation.
What is Gas Limit?
In order to get an operation done in Ethereum, the operation generator (i.e. the person initiating the transaction or the smart contract creator) must specify a gas limit before they submit it to the miners. When a gas limit has been specified only then will the miners start executing the operation.
When submitting a gas limit, the following points must be considered:
- Different operations will have different gas costs (as has been shown before).
- The miners will stop executing the moment the gas runs out.
- If there is any gas left over, it will be immediately refunded to the operation generator.
Let’s see this in operation in a hypothetical scenario:
Suppose, we are adding two numbers and for that, the contract must do the following actions:
- Storing 10 in a variable. Let’s say this operation costs 45 wei gas.
- Adding two variables, let’s say this costs 10 wei gas.
- Storing the result which again costs 45 wei gas.
Suppose the gas limit is 120 wei.
The total gas used by the miner is (45+10+45) 100 wei.
The fees that is owed to them assuming 1 wei costs 0.02 micro ETH is (100*0.02 micro ETH) = 0.000002 ETH.
Now, how much gas is left over?
120 – 100 = 20 wei.
The 20 wei is refunded back to the operation generator.
So what went wrong with KotET?
KotET contracts behaved pretty normally except during one scenario. When the KotET contract sent a payment to a “contract account”, it somehow only allocated a small amount of gas…2300 to be exact. That is clearly not enough gas to cover the expenses.
As KotET puts it,
“When a wallet contract failed to process the payment sent to it by the KotET contract, the ether paid was returned to the KotET contract. The KotET was not aware that the payment had failed and it continued processing, making the caller King despite the compensation payment not having been sent to the previous monarch.”
Let’s try and breakdown everything that went wrong here:
- The gas limit of 2300.
- The mist wallet contract which was insufficient for the payment to be accepted by the wallet contract.
- KotET contract developer was unaware of the 2300 gas limit which was included when sending the payment.
- KotET contract developer was unaware of that any part of the transaction could fail and revert without the whole chain failing and going back.
- Minimal beta testing in the real-world by the KotET contract developers.
- The fallback function in the wallet contracts used by the Ethereum Mist Wallet requires more gas than 2300 available during a <address>.send(<amount>) call.
Looking at the Code
This contract is a simplified representation of the game:
According to the contract, when a participant sends ether to the contract (msg.value), they also trigger KotET’s fallback by default. The KotET fallback first checks if the sent ether is enough to get the title.
If not, it reverts the ether transfer. If yes, the participant is chosen as the new king. When that happens, the compensations is sent to the previous king and the participant is crowned. The difference between the money sent by the new king and the compensation to the old king is kept by the contract. The KotET owner can withdraw all these ethers accumulated in the contract through sweepCommission.
The vulnerability lies I the send function If the function is equipped with insufficient gas, the king.send(compensation) will fail if the king’s address is that of a contract with an expensive fallback. In this case, the compensation is simply kept by the contract instead of the previous king.
We hope that you learnt from KotET’s mistake. It is little mistakes like these which can cause a lot of havoc if not corrected in time. When it comes to smart contract coding, attention to detail is of paramount importance to avoid smart contract vulnerabilities.