Smart Contract Security Class(No.006)

TRON DAO
4 min readJun 19, 2019

In this class, we will review the contents of the previous two classes with a sample vulnerability contract. At the same time, everyone is welcome to follow TRON Foundation official twitter, and submit your contract code thought TRON-Eye.

The following is the contract code from https://troneye.com (hereinafter referred to as TRON-Eye). TRON-Eye is a TRON verification platform from the community. The previous classes have introduced the TRON-Eye verification platform in detail. This contract is RouletteV4 (contract address: TPJyhLPZqbxZqhU5XdCKTRLwM2BdeUHtqn), RouletteV1 (address: https://troneye.com/reveal?address=TYEGUsZKYuYRQtGjq7pMTKLJMqddnmoWpA).

Figure 1 RouletteV4’s source code

The code for the RouletteV0 contract is very short, the core code is as follows.

1. contract RouletteV4 is Owner {

2. uint256 public lastPlayed;

3. uint256 public secretNumber;

4. uint256 public lastBet;

5. uint256 public betPrice = 1000;

6. address public owner = msg.sender;

7.

8. struct Game {

9. uint256 number;

10. uint256 secret;

11. address player;

12. }

13.

14. Game[] public gamesPlayed;

15.

16. constructor() public {

17. lastPlayed = now;

18. shuffle();

19. }

20.

21. function play(uint256 number) payable public {

22. require(msg.value >= betPrice && number <= 10);

23. lastPlayed = now;

24.

25. Game game;

26. game.secret = secretNumber;

27. game.player = msg.sender;

28. game.number = number;

29. gamesPlayed.push(game);

30.

31. if (number == secretNumber) {

32. // win!

33. msg.sender.transfer(address(this).balance);

34. }

35. lastBet = msg.value;

36.

37. shuffle();

38. }

39.

40. function kill() public {

41. require(owner == msg.sender && now > lastPlayed + 1 days);

42. selfdestruct(msg.sender);

43. }

This contract is still roulette. When the contract is initialized, a random number of 1–20 is set: secretNumber. The player tries to guess the number by calling play(). And reset a new random secretNumber. For the sake of demonstration, the contract’s secretNumber is set to public, and multiple query functions are provided.

The contract looks like the player has a 50% chance of guessing. As long as the guess is correct, the user can take all the trx of the contract; at the same time, during the lastPlayed + 1 days, the contract deployer can’t kill the contract. Is that right?

No! In fact, if you play this contract, no matter how many times you play, you will never win. Your bet is accumulated in the contract each time. In addition, the creator of the contract can kill the contract at any time.

Why?

According to what we said in the previous issue, the following 4 lines will overwrite the values of the 0th to 2nd variables of the global.

25. Game game;

26. game.secret = secretNumber;

27. game.player = msg.sender;

28. game.number = number

Now, the puzzle is which variables will be overwritten.

According to the previous issue, the inheritance of solidity is actually a copy of the code, which means that the 0th variable of the contract RouletteV4 is the owner of the Owner contract, not its own lastPlayed.

Speaking of this, it is easy to understand why the player can never win, because line 27 is changed secretNumber to msg.sender before the comparison. At the same time, it cleverly modified lastPlayed to the last secretNumber with 26 lines. Wait, you will definitely ask, isn’t it true that the secretNumber is wrong, and the owner is covered by the 28th line as a number, so why can the contract deployer be able to kill the contract at any time?

This is because:

1. Line 37 generates a new secretNumber with a range of [0–20), which is used by line 26 for the next play;

2. Since RouletteV4 inherits from Owner, both contracts have owner variables, but the owner of RouletteV4 used in line 41 is the owner of line 6 of its own.

Now, you can see that after any player plays once, the lastPlayed is successfully modified to a number within 20. Then you will understand why the require() on line 41 will not be reverted.

Let’s look a little further:

1. If you change the order of lines 26 and 27, can the contract creator still kill the contract?

2. What is the result, if you shift the order of the 9th-11th line?

3. The exact same code, if you remove the inheritance of is Owner, what’s now?

Welcome to follow our twitter and tell me the answer.

This class will be end here. Thanks to TRON-Eye for providing the contract verification tool. More information about them can be found directly on their website https://troneye.com.

We continue to call for source code for follow-up classes, and we welcome your official twitter submissions.

--

--