Roll Back Attack about blacklist in EOS

by yudan@SlowMist security team


Background

On the day 2018–12–19, Most of gambling dapp was suffered roll back attack,including Betdice,EOSMax,Tobet etc. Caluclating at the price of 18 rmb each EOS, it had loss over 500 million rmb. At the same time, during the period, BetDice issued multiple announcements through the lianjinshu platform, which caused panic.

At the same time, the SlowMist security team gave a temporary plan for the exchange and the centralized wallet. At this moment, the attack method is still a mystery. So, what exactly is the attack method? Before you can conduct an attack review, you need to understand a little technical background.

Technical Background

  1. As we know that the consensus algorithm used by EOS is the DPOS algorithm, which uses the way that 21 nodes rotate out of blocks. Except for the 21 nodes, there are no permissions for the block, there role is to broadcast the received transaction, and then the bp(block producer) will package it. Having said that, it is easy to see that if a transaction is sent to all other nodes except the bp, the transaction goes through two processes. First, the transaction is first received by the entire node, and then the transaction is broadcast by the node for packaging. A transaction is not rollable after a node that exceeds 2/3+1 in the bp is confirmed, that is, it is irreversible. This process takes about 3 minutes. That is to say, when the transaction is sent to the full-node except the bp, since the full-node has no package right, the transaction is still in a reversible state at this moment (this assumes the read mode of the node database) For the default speculative, [Reference on reading mode] (https://developers.eos.io/eosio-nodeos/docs/read-modes)). This is a core key point.
  2. Each bp (block producer) can be configured in the blacklist of the config.ini file of its own node. The account in the blacklist cannot be traded, that is to say, blacklisted transactions anyway, will be rolled back.

Blacklist configuration path: 
Mac OS: ~/Library/Application Support/eosio/nodeos/config/config.ini 
Linux: ~/.local/share/eosio/nodeos/config/config.ini

After understanding the above knowledge points, we can review the entire attack.

Review

Track one of the attacker’s attack accounts and find that there is only one transfer function in the account contract.

At the same time, we can find out through the transaction records of this account that this account has only the reveal record, and there is no bet record. It seems that the project party deliberately gives the account EOS. However, this is not the case. Then why is this happening? This requires knowledge of the above technical background. The following are detailed attack techniques:

1.Firstly,attacker call the transfer function of a normal contract which is not a blacklist address through the blacklist address,the function has a inline action to bet for.And the parameter of the function is “from” and “to”,the “from” parameter is expected to be the address that controlled by attacker and can not be a blacklist while the “to” parameter is expected to be the victim address(gambling games).

At this time,the transaction that the attacker sent by was sent to the project side’s node server,and initiate by the blacklist address.

2.On the second step, the node server of project side catched the transaction of the attacker,and executed the reveal function immediately,if won,the project side will send EOS to the address controlled by the attacker.

3.After going through the 1, 2 operation. In theory, the non-blacklist account controlled by the attacker is the balance deduction. Then proceed to the normal draw logic. Everything was normal before I got here. Some readers may ask why the blacklist is configured and the transaction can be initiated normally. The reason is that the blacklist effective range is in bp, and there is no blacklist configuration in the normal full-node config.ini. So the attacker can still initiate a transaction.

4.So far, the attack officially started, and it has reached the most critical place. Since the project side node has completed the lottery logic immediately after receiving the bet transaction, it adopts the offline reveal mode, that is, the betting transaction. And the reveal deal is two different transactions. However, the two transactions were only completed within the project side’s nodes and are still reversible. When the project side’s node broadcasts the two transactions to bp, since the initiator of the first bet transaction is in the blacklist of the bp node, the transaction will be rolled back, that is, the packaging fails, and the launch of the reveal transaction. The project side address is not the blacklist and its trantraction will be packaged normally. Therefore, the first bet transaction in the two transactions will be rolled back, and the reveal transaction will still be packaged, which explains why there is only a reveal record and no betting record. Because the betting record has been rolled back.

The whole process can refer to the following figure:

Attack Reappear

This attack refers the article in the EOS LIVE Wallet team: https://eos.live/detail/19255

1.Environmental preparation

(1) Prepare two nodes locally, one packing node, one synchronous node, the packing node is used to simulate the real bp, and the synchronous node is used to simulate the project side. The packing node needs to open the history plugin to facilitate subsequent debugging. And add the attacker to the node blacklist. Convenient for subsequent debugging. The package node needs to open the automatic reveal plugin. For details on the automatic reveal plugin configuration, see https://github.com/superoneio/security

The code used for this recurrence: https://github.com/superoneio/security

Local multi-node configuration method official reference: https://developers.eos.io/eosio-nodeos/docs/local-multi-node-testnet

(2) Three test accounts, namely tobetioadmin, tobetiologs1, and attackproxy1, respectively, the project party account, the project party log account, and the attack agent account, wherein tobetioadmin deploys the tobet game contract, tobetiologs1 deploys the logs contract, and attackproxy1 deploys the attack contract. Note that the other two accounts except the attack agent account should not be changed to other accounts. If you change to other accounts, you need to modify the automatic reveal plugin. The automatic lottery plugin intercepts the account of tobetioadmin.

(3) Here is my two-node configuration:

nodeos_main is the packing node and nodeos_second is the synchronous node.

2.start the node

Seeing the above information means that the dice_plugin configuration is successful.

3.First test the normal logic.

As you can see, the attack agent contract made a normal transfer.

4.Start the attack, use the blacklist account to call the attack agent contract, and launch an attack to the project side contract.

(1) Query initial balance

(2) In order to ensure the success of the attack, 4 attacks were continuously launched to the project party.

(3) Check the balance again

(4) Query the attacker account record

It can be seen that there is no record of the attacker’s call to attackproxy1. The last two records are the records that I left when I tested the direct blacklist to attack the tobetadmin. Not related to this test. However, it was found through the query that the local record and the on-chain record are consistent, that is, there is no bet record.

(5) Query the account record of attackproxy1

What can be seen is that this also coincides with the record on the chain, only the reveal record, just like tobetadmio deliberately gave the awardproxy1 a general prize.

Through the above recurrence and comparison with the records on the chain, we can prove that the attack method mentioned above is the hacker’s attack method, and the blacklist is used for the rollback operation.

Defense recommendations

1.Defense recommendations for DApp 
The node turns on the read only mode to prevent unacknowledged blocks from appearing on the node server.

2.Establish a dependency of reveal function, such as order dependency. And check if the record is existed on the blockchain when execute reveal function. Even if the reveal function was excuted successful on the node server, since the record is rolled back in bp, the corresponding record will be rolled back.

Acknowledgement

Thanks to the EOS LIVE Wallet team for technical troubleshooting and the provision of the codes during the local reapperence process.

Reference

Node configuration reference:

https://developers.eos.io/eosio-nodeos/docs/read-modes

EOS LIVE Wallet Team article:

https://eos.live/detail/19255