As Ethereum smart contracts find their application mostly in e-commerce applications, we believe these are more commonly vulnerable to attacks. In these smart contracts, we mainly focus on identifying vulnerabilities that programmers and users of smart contracts must avoid. This post aims at explaining these seven application-level security vulnerabilities and their earlier adopted methods of prevention. Our main focus is on the Solidity updates up to the most recent Solidity v0.6.0 that fix some of these vulnerabilities.
A reentrancy attack can drain a smart contract of its ether, can aid an intrusion into the contract code. When an external call function to another untrusted contract is made and an attacker gains control of this untrusted contract, they can make a recursive call back to the original function, unexpectedly repeating transactions that would have otherwise not run, and eventually consume all the gas.
The reentrancy vulnerability exploitation in the DAO attack (as shown in Figure 1) was accomplished in four steps,
- Step 1: The
Attackerinitiates a transaction by calling the
Withdrawfunction of the
- Step 2: The
Victimtransfers the money and calls the
fallbackfunction of the
- Step 3: The
fallbackfunction recursively calls the
withdrawfunction again, i.e., Reentrancy;
- Step 4: Within an iteration bound, extra ether will be transferred multiple times to the
1. 1 Earlier suggested Best-Practice that avoids this vulnerability
Re-entrancy vulnerability can be prevented by ensuring that state changing logic is committed before ether is sent out of the contract through an external call. It is also a good coding practice to put any logic that performs external calls to unknown addresses at the last operation in a program’s execution. This is known as the checks-effects-interactions pattern. Another technique is to use a mutex by adding a state variable which locks the contract during code execution, thus preventing re-entrant function calls.
2. Call to the unknown
When a function invocation or an ether transfer unexpectedly invokes the fallback function of the callee/recipient. Some of the primitives of Solidity language that causes this are:
callused to invoke a function or transfer ether
send, used to transfer ether from the running contract to some other contract
delegatecall, used to invoke a function or transfer ether in the caller environment
- direct call (see listing 1)
If an invoked function’s signature does not match with any existing function, then the call results in a call to the recipient’s
2. 1 Earlier suggested Best-Practice that avoids this vulnerability
Solidity has provision for implementing library contracts by using the keyword library. These library contracts are stateless and non-self-destructive. Forcing libraries to be stateless mitigates attacks whereby attackers modify the state of the library directly to affect the contracts that depend on the library’s code. Therefore, when using call, DelegateCall, the call-to-the-unknown attack that may change the state of the victim contract can be prevented by building stateless libraries.
Solidity version update that mitigates Reentrancy and Call-to-Unknown vulnerabilities
Solidity V0.6.0 splits the logic behind the earlier
fallback function into two different functions, one with the
fallback keyword and a receive ether function defined using the
receive keyword. The receive ether function is implicitly
payable and is called whenever the call data is empty. Whereas, the new fallback function, now with the
fallback keyword, is called when no other function matches. This
fallback function may or may not be
payable. If it is not
payable then transactions not matching any other function which sends value will revert.
This Solidity update would, therefore, solve the issue of unexpected code getting executed when the
fallback function of an external smart contract is called.