In-depth understanding of reentrancy attack vulnerabilities
foreword
foreword
The concept of smart contract (English: Smart contract) was first proposed by Nick Szabo in 1995. It is a computer protocol designed to disseminate, verify or execute contracts in an informatized manner. Trust transactions, which are traceable and irreversible. However, smart contracts are not secure. The Re-Entrance attack vulnerability is one of the attack methods in Ethereum. As early as 2016, the DAO incident caused a hard fork of Ethereum.
first level title
Vulnerability overview
In Ethereum, smart contracts can call the code of other external contracts. Since smart contracts can call external contracts or send Ether, these operations require contracts to submit external calls, so these external calls can be used by attackers to cause attacks Hijacking allows the attacked contract to be re-executed at any position, bypassing the restrictions in the original code, thus re-entry attacks occur. Reentrancy attacks are essentially similar to recursive calls in programming, so they can happen when a contract sends ether to an unknown address.
To put it simply, there are two conditions for a reentrancy attack vulnerability to occur:
An external contract is called and the contract is unsafe

The above code snippet is the simplest withdrawal operation. Next, we will analyze the reasons for the reentrancy attack in detail.
Vulnerability analysis
first level title
Vulnerability analysis
Before formally analyzing reentrancy attacks, let's introduce some key knowledge.
.transfer(),01 Transfer methodSince the re-entry attack will be sent during the transfer operation, the commonly used transfer method in Solidity is- .send() and
- .gas().call.vale()(), the three transfer methods are described below:
- .transfer(): Only 2300 gas will be sent for the call. When the transfer fails, throw will be used to perform the rollback operation, thus preventing reentrancy attacks.
.send(): Only 2300 gas will be sent for calling, and the Boolean value false will be returned when the sending fails, thus preventing reentrancy attacks.
No function was matched when calling the contract;
Fallback function: The fallback function is one and only one function without a name in each contract, and this function has no parameters and no return value, as shown below:

The fallback function is executed in the following situations:
No function was matched when calling the contract;
No data is transmitted;
The smart contract receives ether (in order to accept ether, the fallback function must be marked payable).
No data is transmitted;
The smart contract receives ether (in order to accept ether, the fallback function must be marked payable).

03 Vulnerability code

The following code has a reentrancy attack. It implements a contract similar to a public wallet. All users can use deposit() to deposit into the Reentrance contract, or use withdraw() to withdraw from the Reentrance contract. Of course, everyone can also use balanceof() to query the balance of themselves or others in the contract.

First use an account (0x5B38Da6a701c568545dCfcB03FcB875f56beddC4) to act as the victim, and click the Deploy button to deploy the contract in Remix IDE.

After successfully deploying the contract, fill in 5 in the VALUE setting box, change the unit to ether, and click deposit to deposit 5 ethers.


Click wallet to view the balance of the contract, and find that the balance is 5 ether, indicating that our deposit is successful.

The following code is an attack against the above vulnerable contract:

Use another account (0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2) to act as the attacker, copy the vulnerable contract address to the Deploy setting box, and click Deploy to deploy the above attack contract.


After the deployment is successful, first call the wallet() function to check that the balance of the attack contract is 0.

Call the wallet function of the contract again to check the balance of the vulnerable contract, and find that it has become 6 ether.

The attacker (0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2) calls the attack function of the attack contract to simulate the attack, and then calls the wallet function of the attacked contract to check the balance of the contract, and finds that it has returned to zero. At this time, go back to the attack contract to check the balance, and find that 6 ether in the attacked contract All funds have been withdrawn to the attacker's contract, which causes a reentrancy attack.
04 Source code analysis

first level title
Related cases
text

On June 17, 2016, TheDAO project suffered a reentrancy attack, resulting in more than 3 million Ethereum being separated from TheDAO asset pool, and the attacker used the splitDAO() function in TheDAO smart contract to reuse his own DAO Assets carry out re-entry attacks, constantly separating DAO assets from the asset pool of TheDAO project and transferring them to their own accounts.

The following code is part of the splitDAO() function, whose source code is in TokenCreation.sol, which transfers tokens from the parent DAO to the child DAO. The balance array uint fundsToBeMoved = (balances[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply determines the amount of tokens to be moved.

paidOut[_account] += reward The updated state variable is placed after the payOut function call in the problem code.

Issue a .call.value call to _recipient, transfer _amount Wei, and the .call.value call will use all the current remaining gas by default.
Solution
Through the above analysis of the reentrancy attack, we can find that the key point of the reentrancy attack vulnerability is the use of functions such as fallback to call back itself to cause recursive calls to perform circular transfer operations. Therefore, there are the following solutions to the reentrancy attack vulnerability.
secondary title

01 Use other transfer functions
Use Solidity's built-in transfer() function when sending Ethereum transfers to external addresses, because transfer() will only send 2300 gas to call, which will not be enough to call another contract, use transfer() to rewrite the original The withdraw() of the contract is as follows:

02 Modify the state variable first
This method is to ensure that the modification of the state variable is earlier than the transfer operation, that is, the checks-effects-interactions mode (checks-effects-interactions) officially recommended by Solidity.

03 Use a mutex
There is a security contract specifically for reentrancy attacks in the official OpenZeppelin library:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol




references
first level title
https://zhuanlan.zhihu.com/p/111446792
references
https://blog.csdn.net/henrynote/article/details/82119116
1. Several hard forks of Ethereum:
https://blog.csdn.net/Flyhps/article/details/83095036

