In recent days, the theft of the currency security has once again triggered everyone’s concern about digital currency security. In fact, cyber theft is very common and has triggered an escalation of technology. As a representative of Blockchain 2.0, Ethereum has been suffering from various attacks since its birth. To this end, this article discusses the Ethereum's empty account DoS attack incident, to talk about how Ethereum technically guarantees security.
Ethereum suffered a series of DoS attacks in September and October 2016, and the attackers created 19 million empty accounts at a very low cost within the Ethereum network. Empty accounts waste disk space, increase synchronization time and slow processing time, which led to the October 2016 "EIP-150 Hard Fork" hard fork. And the impact of the attack on the "Spurious Dragon" fork in November was further fixed.
What is an empty account?
An empty account is an account with zero balance, zero constant and empty code (each account in Ethereum has a constant, the constant is equal to the number of transactions associated with this account). An empty account is functionally equivalent to an account that does not exist. The only practical difference is that the empty account needs to be stored in the Ethereum state tree. An empty account is easy to generate. You only need to send 0 or any Ethereum to a non-existent account. The non-existing account will become an empty account. In theory, if the miner accepts a transaction fee of 0, he can even send the transaction from an empty account or an account that does not exist.
What is wrong with having 19 million empty accounts in the Ethereum tree?
Empty accounts are stored in the Ethereum state tree
· They waste storage space
· They increase synchronization time, especially fast sync and warp sync.
· They slow down transaction processing because storing most or all of the state in RAM becomes more difficult and therefore requires more disk access.
The attacker established a parent contract, which was called 4,750 times. The method of this parent contract is as follows:
(1) Create a sub-contract
0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000
0021 PUSH1 0x00
0024 PUSH1 0x20
0026 PUSH1 0x00
0028 PUSH1 0x00
This will create a subcontract on a new address.
;; Child contract (in full)
The code for the subcontract is in PUSH32, ie:
0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000
The first 12 bytes are functions that construct subcontracts, and the next 4 bytes are the code for subcontracts.
(2) load a counter in the store
002b PUSH1 0x00
This will track the empty account being created and allow subsequent calls to the parent contract to continue generating empty accounts from the previous stopped location.
(3) The "self-destruct function" of the sub-contract is called cyclically 40 times.
(The following code will be cycled 40 times)
0030 PUSH1 0x01
0034 PUSH1 0x00
0037 PUSH1 0x00
003a PUSH1 0x20
003f PUSH1 0x06
First add one to the counter, copy it, put a copy back into memory, and then call the subcontract with the counter as the call data. A subcontract interprets the data (counter) it sends as an address, and when it self-destructs, it sends its (zero) value to that address, creating an empty account.
Selfdestruct(address) "self-destruct function" will transfer the balance of the contract to the specified address, "self-destruction function" function has a small number of Gas, this design is to encourage people to destroy the smart contract that is no longer used, after destruction The status of the contract will disappear from the state store. When you no longer use a contract, you call the "self-destruct function", which is more cost-effective than calling Transfer to transfer the money out of the contract.
The key point here is that the subcontract does not actually self-destruct before the end of the current transaction, so it can be called multiple times in a single transaction. The subcontract is deleted when the current transaction is terminated. Each time the parent contract is invoked, the child contract is recreated, and then its "self-destruct function" can be called multiple times internally and finally destroyed.
(4) Check the remaining Gas
If there is enough Gas, go back and re-traverse all 40 calls and continue to increase the address counter.
0329 PUSH2 0x6000
032d PUSH3 0x00002f
Therefore, each call to the parent contract can create thousands of empty accounts, depending on the amount of Gas initially supplied.
(5) Store the counter to prepare for the next call
0332 PUSH1 0x00
Creating an account with the "self-destruct function" is extremely low cost.
Who will carry out this DoS attack?
There are attack opportunities in network protocols. This is very common. No one in the real world will do it because they have no incentive to exploit these exploits. Sadly, we are worried that someone will launch such an attack against Ethereum.
The most dangerous group is “griefers”, who may launch a DoS attack in order to short the Ethereum, in order to let the price of the Ethanol fall in the short term. Similarly, extremists believe that cryptocurrencies are a zero-sum game and they may also be involved in destruction. Of course, those who want to attack the Ethereum miners may also be involved. Because at present, there is no cost to this kind of attack, these groups are likely to launch attacks.
Ethereum's defense measures
- Block such attacks
Adjust the amount of Gas to make the "self-destruction function" cost higher. Ethereum conducted an "EIP-150 Hard Fork" hard fork. On October 18, 2016, at 23:19:31 2463000. In addition to other adjustments in the amount of Gas, the "self-destruct function" was adjusted to 5000 Gas (originally 0 Gas).
- Clear previously created empty accounts
Later, Ethereum made a fork called "Spurious Dragon." At 01:15:44 2675000 on November 23, 2016, the relevant change in this fork is the EIP 161 status sweep, in order to clear the previously created empty account. The actual operation of clearing the account is actually done after the fork, and the empty account will be automatically deleted when it is "touched" by a transaction. The Ethereum Foundation will systematically begin the "CALL" of these empty accounts generated by the attack to gradually complete the cleaning.
Summary: When a transaction "touches" an "empty account", the "empty account" will be deleted.
· "Empty Account": If the account has no code and the constant is 0 and the balance is 0, the account is considered empty. (Every account in Ethereum has a constant, the constant is equal to the number of transactions associated with this account)
· “Touch”: When an account involves a “state change” operation, it is considered to be “touched”. This includes, but is not limited to, being transferred to 0 Ethereum.
· "Status change" of the account:
o When it is the target of the "SUICIDE" operation or the refund address. ("SUICIDE" is the later "selfdestruct", because suicide is a heavy subject, so it changed to self-destruction)
o When it is the target or source of a "CALL" operation or message-call.
o When it is the target or source of a "CREATE" operation or contract creation operation.
o As a miner, it is a block reward or a transaction fee recipient.
· A trader will execute a suicide list immediately before a transaction receipt is generated. Therefore, if there is a situation where an empty account is touched, the empty account will be committed.
In fact, the current implementation only needs to track four scenarios:
· An empty account with zero-valued transactions passed to it via CALL;
· An empty account, there is a transaction transmitted to the zero value through SUICIDE;
· An empty account with zero value passed to it via a message-call transaction;
· An empty account transfers zero value to it via a zero-gas-price fees transfer. (General Gas Price is set to zero for the trade, the miner won't dig, but the miner may dig the transaction for the 0 transaction fee sent by himself)