Education
Re-entrancy Attacks: What every blockchain user must know
Published
1 year agoon
By
Kyrian AlexHave you ever wondered if your investments on the blockchain are truly secure?
Picture this: a seemingly harmless smart contract, just waiting for your interaction. Little do you know, within its lines of code lies a devastating vulnerability, eagerly waiting to exploit your trust.
In this eye-opening article, we will expose a lurking threat, equip you with the knowledge to protect yourself and empower you to navigate the treacherous waters of the blockchain with confidence.
Understanding the Anatomy of a Re-entrancy Attack
Smart contracts have extended the capabilities of blockchain beyond simple transactions. Smart contracts are self-executing agreements with the terms of the agreement directly written into code. They automatically execute when predefined conditions are met, providing a tamper-proof and efficient way to facilitate complex transactions and automate processes.
While smart contracts offer numerous advantages, they are not immune to vulnerabilities. The decentralized nature and immutable characteristics of blockchain make it difficult to rectify errors once deployed. Smart contracts are executed on the blockchain, and any flaws or vulnerabilities in the code can have severe consequences.
One such vulnerability that has been exploited by malicious actors is the re-entrancy attack.
Reentrancy is a state synchronization problem that arises in smart contracts. When an external call is made from one contract to another, the execution flow control is transferred to the called contract. It is important for the calling contract to ensure that all globally shared states are fully synchronized before transferring control.
Since the Ethereum Virtual Machine (EVM) is a single-threaded machine, if a function fails to fully synchronize the state before transferring execution control, it can be re-entered with the same state as if it were being called for the first time. This can lead to unintended consequences, such as executing actions repeatedly that were intended to be executed only once.
To understand the issue better, let’s consider an example using the WETH contract.
WETH is a contract that wraps the native asset Ether into an ERC20-compatible token. In the deposit function of the WETH contract, when a user sends Ether, their balance is increased and stored in the balanceOf mapping.
When a user wants to convert their WETH back to Ether, they call the withdraw function. Inside the withdraw function, a low-level call is used to transfer Ether to the user’s address, which transfers the execution flow to the receiver.
In this example, the external call is made before updating the balance. If the caller is an externally owned account (EOA), the transfer completes successfully, and execution continues within the withdraw function. However, if the caller is a smart contract, the default payable function of the contract is invoked, and this function can be controlled to perform arbitrary actions.
To exploit this function, all an attacker requires is to have a certain balance associated with their smart contract address and establish a fallback function that invokes the withdraw operation.
Once the transfer of funds is correctly executed using “msg.sender.call.value(amount)()”, the attacker’s fallback function can repeatedly trigger the withdraw operation, transferring additional funds, even before the condition “balances[msg.sender] = 0” can prevent further transfers. This cycle persists until either all the Ether is depleted or the execution hits the maximum stack size.
Typically, a susceptible function involves making an external call using transfer, send, or call. The distinctions among these functions will be discussed in the section addressing the prevention of reentrancy attacks.
Now, during the execution of the default payable function, the WETH contract is unaware that it has already sent the Ether because the balanceOf mapping has not been modified yet. If the malicious contract calls back into the withdraw function, the require statement that checks the WETH balance to withdraw Ether will pass. This way, the attacker can exploit the reentrancy vulnerability and gain access to infinite Ether by repeatedly executing the function.
Thus, Reentrancy attacks refers to a vulnerability in smart contracts that allows an attacker to repeatedly enter and execute a particular section of code within a contract before it has a chance to complete its previous execution.
It’s important to note that in Solidity versions 0.8.0 and above, underflow/overflow checks are performed by default. As a result, once all reentrant calls are resolved, the balanceOf mapping will be decreased according to the number of times the function was called. If the balance underflows in Solidity versions below 0.8.0, the attacker will end up with a very large balance, exploiting another vulnerability.
Types of re-entrancy attacks
There are several types of reentrancy attacks, each with its own characteristics and potential impact:
1. Single-function Reentrancy
This is the most basic form of reentrancy. It involves a contract calling an external function before finalizing its state changes, and that external function subsequently reentering the original contract’s function. The infamous DAO Hack of $60 million and the subsequent Ethereum network hard fork are examples of this type of reentrancy attack.
2. Cross-function Reentrancy
In this scenario, an attacker exploits two different functions within a contract that share the same state. By making an external call before the shared data is updated, the attacker can enter the second function with the unchanged state, potentially gaining unauthorized access or manipulating the contract’s behaviour. The OpenZeppelin’s reentrancy guard can help mitigate this issue by implementing a nonReentrant guard in both functions, preventing reentry within the same call frame.
3. Cross-contract Reentrancy
Reentrancy is not limited to functions within the same contract; it can also occur across multiple contracts that share the same state. If the shared state is not updated before an external call, reentrancy can introduce critical vulnerabilities. Employing patterns like the Checks-Effects-Interactions (CEI) pattern can help prevent such risks.
4. Read-Only Reentrancy
Although auditors and bug hunters typically focus on entry points that modify state, read-only reentrancy can still be a concern. This type of reentrancy occurs when a protocol relies on reading the state of another. For example, if a pricing mechanism depends on an external price oracle, reentering the view function during a critical operation can lead to attacks. Projects integrating price oracles or liquidity management protocols should exercise caution to avoid such vulnerabilities.
5. Cross-Chain Reentrancy
With the rise of cross-chain messaging protocols, a new form of reentrancy attack has emerged. Cross-chain reentrancy can occur when bridging assets between chains or utilizing cross-chain messaging. Although there have been no documented instances of cross-chain reentrancy attacks in the wild, it is crucial for protocols involved in cross-chain operations to assess and address this potential threat.
What can trigger reentrancy?
- Low level calls (.call())
- transfer of ERC223 tokens
- transferAndCall of ERC667 tokens
- transfer of ERC777 tokens
- safe* transfer functions of ERC1155 tokens
- *AndCall functions of ERC1363 tokens
- safe* functions such as safeTransfer or safeMint of ERC721 tokens
- transfer of certain ERC20 tokens which may have implemented a custom callback function for receivers
How to prevent re-entrancy attacks
To prevent reentrancy, several measures can be taken.
1. Using the `send` and `transfer` functions:
These functions limit the amount of gas forwarded to the receiver, restricting the logic that can be executed. This mitigates gas griefing risks and prevents reentrancy since the inner call will run out of gas before performing the necessary logic.
However, using send
and transfer
can break composability with other smart contracts that rely on logic in the fallback function, such as proxies that delegate their logic to an implementation contract. Hence, it is not recommended to use send
and transfer
if following best practices.
2. Implementing the checks-effects-interactions (CEI) pattern
This is perhaps the most recommended and simplest way to prevent reentrancy. In this pattern, functions that execute external calls should ensure that all external interactions occur after any checks or state changes. This pattern is also known as the tail call pattern in traditional concurrent programming. To fix the previous example in the withdraw function of WETH, we would first perform the require statement to check the user’s WETH balance (check), update the balance in storage (effect), and then make the external call to transfer the funds to the user (interaction).
3. reentrancyGuard
If there are concerns about unknown risks in a permissionless protocol, a reentrancyGuard can be used as a way to ensure that a function cannot be called more than once within the same call frame. OpenZeppelin provides a library for implementing ReentrancyGuards.
However, it’s important to note that using a reentrancy guard incurs an extra gas cost due to the need for performing SLOAD and SSTORE operations to check if the function has already been called. This increased gas cost may not be necessary if recommended patterns, such as the CEI pattern, are followed. Additionally, it’s worth mentioning that a reentrancy guard of this type will not protect against cross-contract reentrancy attacks.
4. Best Practices for Contract Development
Implementing secure coding practices is crucial. Developers should follow established guidelines and standards, such as the Solidity language’s best practices, to minimize the risk of re-entrancy vulnerabilities. This includes carefully managing external function calls and ensuring that state changes occur before any external calls are made.
Furthermore, there are initiatives to disable reentrancy by default at the compiler level, adding an extra layer of protection against reentrancy attacks. Programming languages like Vyper and Solidity are considering implementing this feature, which would make it easier for developers to write secure contracts and create a shift in how external calls are considered within smart contract development.
5. Security Audits and Code Reviews
Conducting thorough security audits and code reviews by experienced professionals can identify potential vulnerabilities, including re-entrancy risks. These audits can uncover overlooked flaws, provide valuable insights, and suggest improvements to strengthen the security of smart contracts.
6. Gas Limits and Mutexes
Setting appropriate gas limits for functions can prevent re-entrancy attacks by limiting the amount of gas available for external calls. Additionally, implementing mutexes (mutual exclusion locks) can prevent multiple re-entrant calls from executing simultaneously, ensuring that a function completes its execution before allowing another invocation.
Recursive Calls and Gas Deduction
One of the key conditions that enable re-entrancy attacks is the presence of recursive calls within a smart contract. Recursive calls occur when a contract invokes another contract within its execution flow. This can create a potential vulnerability if not implemented carefully.
In the context of re-entrancy attacks, recursive calls allow an attacker to repeatedly call back into the same vulnerable contract before the previous call completes. This can lead to unexpected behaviour, as the state of the contract may not be fully updated or validated during each call, allowing the attacker to manipulate the contract’s logic and potentially drain funds or cause other unintended consequences.
The gas deduction, a mechanism used in blockchain platforms, plays a role in re-entrancy attacks as well. Gas is a unit of computational effort required to execute operations on the blockchain. Contracts pay for each operation they execute by deducting gas from their allocated balance. However, in the case of a re-entrancy attack, an attacker can exploit a contract’s vulnerability to repeatedly call a malicious external contract and drain the victim contract’s gas, effectively halting its execution and allowing the attacker to manipulate the flow of funds.
Storage Manipulation and Re-Entrancy Attacks
Smart contracts rely on storage to maintain their state and store critical data. Re-entrancy attacks can leverage vulnerabilities related to storage manipulation to exploit the contract’s logic.
In some cases, a contract may update its storage variables after an external call, assuming that the external contract won’t alter the storage state until the execution is complete. However, an attacker can abuse this assumption by calling back into the vulnerable contract before the state changes take effect. This allows the attacker to manipulate the contract’s storage in unintended ways, potentially leading to fund transfers, unauthorized access, or other malicious actions.
Unintended External Calls and Re-Entrancy Vulnerabilities
Unintended external calls refer to situations where a contract initiates an external call unintentionally or fails to handle the return values properly. These unintended calls can introduce vulnerabilities that can be exploited by attackers to carry out re-entrancy attacks.
For example, a contract might make an external call to a contract address provided as a parameter without validating its integrity. This can enable an attacker to substitute the intended contract address with a malicious one that executes an undesired function or manipulates the contract’s state during the call.
Furthermore, if a contract fails to handle the return values of an external call correctly, it may allow an attacker to re-enter the contract before the necessary checks are performed. This can lead to unexpected behaviour and potentially compromise the security and integrity of the contract.
Understanding these exploitable conditions is essential for developers and auditors to identify and mitigate vulnerabilities in smart contracts, reducing the risk of re-entrancy attacks. Implementing proper checks, validating external contracts, and ensuring the correct handling of external calls are crucial steps in bolstering the security of smart contracts on the blockchain.
Real-World Examples of Re-Entrancy Attacks
Re-entrancy attacks have not been limited to theoretical discussions or hypothetical scenarios. They have manifested themselves in real-world exploits, causing significant financial losses and shaking the trust of blockchain users. Here are some notable instances where projects fell victim to reentrancy attacks in 2023 alone:
- Midas Capital attack – 15 January 2023
- 2Pi Network attack – 15 January 2023
- Abracadabra Money white hat attack – 16 January 2023
- Orion Protocol attack – 2 February 2023
- dForce Network attack3 – 9 February 2023
- Dynamic attack – 22 February 2023
- Sentiment attack – 4 April 2023
- Paribus attack – 11 April 2023
The Implications of Re-Entrancy Attacks
The ramifications of re-entrancy attacks extend beyond immediate financial losses. They strike at the heart of trust and confidence in the blockchain ecosystem. When users witness the exploitation of vulnerabilities, doubts about the security and reliability of blockchain applications inevitably emerge.
Financial losses resulting from re-entrancy attacks can be devastating for individuals and businesses. Savings, investments, and hard-earned funds can vanish in an instant, causing emotional distress and financial ruin. The fear of such attacks may deter potential users from engaging with blockchain technology, stifling its adoption and hampering the growth of the entire ecosystem.
Addressing re-entrancy vulnerabilities is not an option; it is an imperative. The urgency lies in safeguarding the promise of blockchain technology and ensuring that it remains a trustworthy and secure platform for innovation. Now is the time to take action and fortify the defences against re-entrancy attacks.
Here is a list of some of the best tools for locating those vulnerabilities:
To identify potential entry points for reentrancy vulnerabilities, various standards and tools can be utilized.
Future Perspectives
As the blockchain ecosystem continues to evolve, so do the threats it faces. Re-entrancy attacks are just one example of the ever-changing landscape of security vulnerabilities. However, through concerted efforts and continuous advancements in security practices, we can pave the way for a more secure future.
The future holds promise for detecting and preventing re-entrancy attacks. Innovations such as formal verification techniques, enhanced code analyzers, and security-focused programming languages are emerging to bolster the security of smart contracts. Collaborative initiatives between researchers, developers, and blockchain enthusiasts are fostering an environment of shared knowledge and collective defence against threats.
It’s worth noting that the Ethereum Improvement Proposal EIP-1153 aims to reduce the cost of implementing reentrancy guards by introducing new opcodes for data that is discarded after every transaction. These opcodes could potentially alleviate some of the gas cost concerns associated with reentrancy protection.
Conclusion
The spectre of re-entrancy attacks looms over the blockchain, posing a significant threat to its integrity and user trust. By understanding the mechanics of these attacks, learning from real-world examples, and implementing effective mitigation strategies, we can fortify the blockchain ecosystem against this insidious threat. The urgency to address re-entrancy vulnerabilities cannot be overstated. It is imperative that we act now, employing robust security measures, fostering community awareness, and staying at the forefront of technological advancements to secure the future of blockchain technology. Together, we can build a safer, more resilient blockchain ecosystem that paves the way for widespread adoption and transformative innovation.
References:
- https://docs.soliditylang.org/en/v0.4.21/security-considerations.html#use-the-checks-effects-interactions-pattern
- https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard
- https://docs.soliditylang.org/en/v0.4.21/security-considerations.html#re-entrancy
- https://twitter.com/_prestwich/status/1251382098188877824
- https://medium.com/consensys-diligence/uniswap-audit-b90335ac007
- https://peckshield.medium.com/uniswap-lendf-me-hacks-root-cause-and-loss-analysis-50f3263dcc09
Read also:
https://cryptotvplus.com/2023/01/midas-capital-loses-660000-in-a-jassets-flash-loan-attack/