A Jedi’s guide to Solidity reentrancy attacks

Mark Mathis
Coinmonks
Published in
3 min readMar 27, 2018

--

Writing a solidity contract is one thing, but making sure that the participant’s funds are safe is another. Solidity attempts to enforce as much as possible through its compilation process, but it is up to the creator to take it across the finish line.

In our previous article, we created a UI for our betting dApp that Master Obi-Wan and Yoda use to get their gambling fix. In this article we will work on examining the smart contract for any hidden security bugs or attack vectors.

Security

Everyone agrees that security is an issue that is absolutely critical to the success of mass blockchain adoption. We will use the following method to examine our smart contract for security bugs:

  • Remix — We have already used this tool extensively in a previous article, but have not taken advantage of its analysis tab.

Remix

Opening up our Bet.sol smart contract in remix, we can see several issues in the analysis tab. The one that interests us at this point is the potential re-entrancy vulnerability.

A re-entrancy condition is when an actor can call your contract’s function and potentially re-enter the contract before the previous call is completed one or multiple times. This would be especially disastrous in the case of a payable function as is our case here.

Bet.sol — vulnerable code

Our code checks for the executing address to be either a bet taker or originator on line 4 — which is good. We have eliminated most of the bad actors here. However, what if one of the players is a bad actor who has seen a vulnerability in the contract and is only participating as a means of exploiting that vulnerability. Our transfer function calls on lines 7, 8, 11, 13, 15, and 16 are vulnerable to such an attack. If you notice that I wrote the transfer amount based on the logic that the pot or total winnings would be roughly equal to the player’s bet amounts multiplied by two. I do not keep up with the amount that has been paid out to each player. So, in the case that a player gets a payout, they could call the contract again recursively and there would be no check in place to see whether they had already been paid or not. They could essentially do this until they ran out of gas or the contract was drained of its eth. This would be something that you would do with another smart contract interacting with this Bet.sol where you would have a default fallback payable function that would call the payout function again before the original call returned.

Bet.sol — Fixed

What we want to do is something called, optimistic accounting, where we deduct the payout amount from the player’s funds represented by a stored number before we send the transfer function. This will prevent the reentrant behavior and still allow the deduction to be reverted in the case of an exception. We want to do this in general with solidity contract state as well to prevent reentrant attacks. Note that I put the resetGame() and getBetOutcome() methods before the calls to transfer winnings. This is safe because any errors with the transfer function(s) will revert our contract state anyways.

Get Best Software Deals Directly In Your Inbox

Additional Resources:

--

--