Vulnerabilities smart contracts Etherium. Code examples

With this post I begin a series of articles on the topic of security of Ethereum smart contracts. I think this topic is very relevant, since the number of developers is growing avalanche-like, and there is no one to protect them from the “rake”. For now - translations ...

1. Scanning Live Ethereum of Unchecked-Send Error Contracts


Original - Scanning Live Ethereum Contracts for the "Unchecked-Send ..."


Authors: Zikai Alex Wen and Andrew Miller

Programming smart contracts in Ethereum is known to be error prone  [1]  . We recently saw several
high-end smart contracts, such as  King of the Ether  and  The DAO-1.0 , contain vulnerabilities caused by programming errors.

Since March 2015, smart contract programmers have been warned about the specific programming hazards that can arise when contracts send messages to each other [6]

Several programming guides recommend how to avoid common mistakes (in official Ethereum documents  [3]  and in an independent manual from UMD  [2] ). Although these dangers are sufficiently clear to avoid them, the consequences of such an error are terrible: money can be blocked, lost or stolen.

How common are the errors resulting from these hazards? Are there still vulnerable, but live contracts for the block chain Ethereum? In this article, we answer this question by analyzing contracts on the live block-chain Ethereum using a new analysis tool that we developed.


What is the "unchecked-send" error?


To send a broadcast to another address, the easiest way is to use the send keyword . It acts as a method defined for each object. For example, the following code snippet can be found in a smart contract that implements a board game.


 /*** Listing 1 ***/ 
if (gameHasEnded && !( prizePaidOut ) ) {
  winner.send(1000); // отправить выигрыш победителю
  prizePaidOut = True;
}

The problem here is that the send method    may fail. If it does not work, the winner will not receive the money, but the variable prizePaidOut will be set to True.

There are two different cases in which the winner.send () function may fail. We will make out the difference between them later. The first case is that the address of the  winner is a contract (and not a user account), and the code of this contract generates an exception (for example, if it uses too much “gas”). If this is the case, then perhaps in this case it is a “winner’s fault”. The second case is less obvious. Ethereum virtual machine has a limited resource called “ callstack”(Call stack depth), and this resource can be used by another contract code that was executed earlier in the transaction. If the callstack is   already used up at the time the send command   is executed, the command will fail, no matter how the winner is defined . The prize of the winner will be destroyed not through his fault! 



How can you avoid this error?

The Ethereum documentation contains a brief warning about this potential hazard  [3] : "There is some danger when using  send — the transfer fails if the call stack has a depth of 1024 (this can always be caused by the caller), and fails if the recipient ends with “gas.” Therefore, to ensure safe transmission of air, always check the return value of  send  or even better: use a template in which the recipient withdraws money. "

Two sentences. The first is to check the return value of  send to see if it completed successfully. If not, then generate an exception to roll the state back.


  /*** Listing 2 ***/
if (gameHasEnded && !( prizePaidOut ) ) {
  if (winner.send(1000))
    prizePaidOut = True;
  else throw;
}

This is an adequate fix for the current example, but not always the right solution. Suppose we modify our example so that when the game is over, the winner and the loser roll back their status. An obvious use of the “official” solution would be the following:


/*** Listing 3 ***/
if (gameHasEnded && !( prizePaidOut ) ) {
  if (winner.send(1000) && loser.send(10))
    prizePaidOut = True;
  else throw; 
}

However, this is a mistake because it introduces an additional vulnerability. While this code protects the  winner from a callstack attack , it also makes the  winner and  loser vulnerable to each other. In this case, we want to prevent the callstack attack , but continue to execute if the send command  for any reason does not work.

Therefore, even the best best practice (recommended in our “Programmer’s Guide for Ethereum and Serpent”, although it is equally applicable to Solidity), is to check the availability of the callstack resource . We can define a callStackIsEmpty () macro that will return an error if and only ifcallstack is empty.


/*** Listing 4 ***/
if (gameHasEnded && !( prizePaidOut ) ) {
  if (callStackIsEmpty()) throw;
    winner.send(1000)
    loser.send(10)
    prizePaidOut = True;  
    }

Better yet, the recommendation from the Ethereum documentation - “Use a template in which the recipient takes the money” is a bit mysterious, but has an explanation. The suggestion is to reorganize your code so that the effect of a send failure is  isolated, and only affects one recipient at a time. Below is an example of this approach. However, this tip is also anti-pattern. It assumes responsibility for checking the callstack by the recipients themselves, which makes it likely that they will fall into the same trap.


/*** Listing 5 ***/
if (gameHasEnded && !( prizePaidOut ) ) {
  accounts[winner] += 1000
  accounts[loser] += 10
  prizePaidOut = True;
  }
 ...
function withdraw(amount) {
if (accounts[msg.sender] >= amount) {
  msg.sender.send(amount);
  accounts[msg.sender] -= amount;
  }
}

Many highly intelligent contracts are vulnerable. Lottery "King of the Ether of the Throne" - the most famous case of this error  [4]  . This error was not noticed until the amount of 200 airs (worth more than $ 2,000 at today's price) could not get a legitimate lottery winner. The corresponding code in King of the Ether is similar to the code in Listing 2 Fortunately, in this case, the contract developer was able to use the unrelated function in the contract as a “manual override” to release the stuck means. A less scrupulous administrator could use the same function to steal the air!


Continued Scanning Live Ethereum of Unchecked-Send Error Contracts. Part 2

Also popular now: