{"id":759,"date":"2025-04-25T13:38:58","date_gmt":"2025-04-25T11:38:58","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=759"},"modified":"2025-04-25T13:38:58","modified_gmt":"2025-04-25T11:38:58","slug":"reentrancy-attack-in-erc-777","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/reentrancy-attack-in-erc-777\/","title":{"rendered":"Reentrancy Attack in ERC-777"},"content":{"rendered":"<p class=\"code-line\" dir=\"auto\" data-line=\"2\">In this blog, we describe reentrancy attacks in the ERC-777 standard.<\/p>\n<p>A hands-on <a href=\"https:\/\/github.com\/Ackee-Blockchain\/reentrancy-examples\/tree\/master\/contracts\/erc777\">example contracts and descriptions is here<\/a>, and <a href=\"https:\/\/github.com\/Ackee-Blockchain\/reentrancy-examples\/blob\/master\/tests\/test_6_erc777.py\">the executable test is here<\/a>.<\/p>\n<p dir=\"auto\" data-line=\"2\">Clone this repository and run <code class=\"codehl\">$wake up<\/code>. Then, run <code class=\"codehl\">$wake test tests\/test_6_erc777.py<\/code> to run on your local environment.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"4\">The ERC-777 is a standard for fungible tokens with a transfer hook. The exchange contract allows users to exchange ETH to <code class=\"codehl\">SSSToken<\/code> at a calculated rate.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"8\">The calculation uses the total amount of <code class=\"codehl\">SSSToken<\/code> in the Exchange contract, the total amount of ETH in the Exchange contract with the corresponding token amount that the user wants to exchange.<\/p>\n<h2 id=\"expected-usage\" class=\"code-line\" dir=\"auto\" data-line=\"10\">Expected usage<\/h2>\n<ul class=\"code-line\" dir=\"auto\" data-line=\"14\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"14\">\n<p class=\"code-line\" dir=\"auto\" data-line=\"14\">Users can call the <code class=\"codehl\">tokenToEthInput<\/code> function to exchange the <code class=\"codehl\">SSSToken<\/code> to ETH.<\/p>\n<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"16\">\n<p class=\"code-line\" dir=\"auto\" data-line=\"16\">Users can call the <code class=\"codehl\">ethToTokenInput<\/code> function to convert ETH to the <code class=\"codehl\">SSSToken<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>This is a vulnerable Exchange contract.<br \/>\n<script src=\"https:\/\/gist.github.com\/MeditationDuck\/c601e1305418337d472ae1a0be508d20.js\"><\/script><\/p>\n<h3 id=\"token-contract\" class=\"code-line\" dir=\"auto\" data-line=\"84\">Token contract<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"86\">We use <code class=\"codehl\">MyERC777Token<\/code> as an ERC-777 token and deploy with the <code class=\"codehl\">Exchange<\/code>\u00a0vault as the\u00a0<code class=\"codehl\">defaultOperator<\/code>. Same as the default usage of ERC-777 \u2013 this <code class=\"codehl\">send<\/code> confirms that the target contract implemented ERC-777, so it can prevent lockout of the values.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/MeditationDuck\/750a88467f6b77eb874d03591ac48f69.js\"><\/script><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"106\"><strong>The ERC777 feature enables the attack<\/strong>. When transferring the token, it does an external call. The target of this external call is the transfer target. This feature is for notifying of balance changes, but this feature is vulnerable to reentrancy attacks.<\/p>\n<h2 id=\"attack-example\" class=\"code-line\" dir=\"auto\" data-line=\"109\">Attack example<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"111\">In this case, we can attack the <code class=\"codehl\">tokenToEthInput<\/code> function. It already has the external call <code class=\"codehl\">recipient.call{value: ethBought}(&quot;&quot;);<\/code> at the end of the function. Moreover, other computations are done above this function. Therefore, it seems the Checks-Effects-Interactions prevention is done.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"113\">However, according to the ERC777 feature, it is vulnerable to attack. <strong>There is an external call<\/strong> when transferring the <code class=\"codehl\">SSSToken<\/code>. Therefore, <strong>we can reenter the contract<\/strong> without sending the ETH value.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"116\">So while re-entering, <code class=\"codehl\">Exchange.balance<\/code> i.e. the balance of ETH in Exchange does not change. This balance value is used for computing the <code class=\"codehl\">getInputPrice<\/code>\u00a0function.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"118\">In\u00a0<code class=\"codehl\">getInputPrice<\/code> function, the calculation is done by this formula.<\/p>\n<blockquote class=\"code-line\" dir=\"auto\" data-line=\"120\">\n<p class=\"code-line\" dir=\"auto\" data-line=\"120\">ETHVAULT = SSSTOKENVALUE * <em>997 * <\/em>ETH_BALANCE_IN_EXCHANGE \/ (SSSTOKEN_IN_EXCHANGE<em>1000 + SSSTOKENVALUE<\/em>997)<\/p>\n<\/blockquote>\n<p class=\"code-line\" dir=\"auto\" data-line=\"122\">And the\u00a0<code class=\"codehl\">ETH_BALANCE_IN_EXCHANGE<\/code> is higher than it should be, and <code class=\"codehl\">ETHVALUE<\/code> is sent to the attacker.<\/p>\n<h3 id=\"attacker-contract\" class=\"code-line\" dir=\"auto\" data-line=\"124\">Attacker contract<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"126\">This is the attack contract.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/MeditationDuck\/d26601c6e888cb606a5b36703c04f5b9.js\"><\/script><\/p>\n<h3 id=\"wake-code\" class=\"code-line\" dir=\"auto\" data-line=\"193\">Exploit<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"195\">This is the exploit.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/MeditationDuck\/5c63e89b0ac7fb68e2ef9f73d6adbbcc.js\"><\/script><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"242\">This is the beginning of Wake&#8217;s output. We exchanged to MyERC777Token and we got 90661089388014913158 tokens from 100ETH.<\/p>\n<p class=\"code-line code-active-line\" dir=\"auto\" data-line=\"245\">This is how it exchanges tokens for ETH using reentrancy.<\/p>\n<p dir=\"auto\" data-line=\"245\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-760\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09.png\" alt=\"\" width=\"1474\" height=\"926\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09.png 1474w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09-300x188.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09-1024x643.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09-768x482.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09-370x232.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-09-760x477.png 760w\" sizes=\"auto, (max-width: 1474px) 100vw, 1474px\" \/><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"304\">This is the ending part of Wake&#8217;s output. It sends 1.2ETH every time from the Exchange contract to the attacker.<\/p>\n<p dir=\"auto\" data-line=\"304\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-761\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36.png\" alt=\"\" width=\"1474\" height=\"926\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36.png 1474w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36-300x188.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36-1024x643.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36-768x482.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36-370x232.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Screenshot-from-2024-06-18-12-57-36-760x477.png 760w\" sizes=\"auto, (max-width: 1474px) 100vw, 1474px\" \/><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"356\">In the end, the attacker received around 9 ETH from this attack.<\/p>\n<h2 id=\"prevention\" class=\"code-line\" dir=\"auto\" data-line=\"358\">Prevention<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"360\">A simple reentrancy guard would prevent this attack:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/MeditationDuck\/30d299c8b49355d183b41f9b72e57fc5.js\"><\/script><\/p>\n<h2 id=\"conclusion\" class=\"code-line\" dir=\"auto\" data-line=\"362\">Conclusion<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"363\">The ERC-777 has an external call to the target. It breaks the Checks-Effects-Interactions patterns and it can easily be vulnerable.<\/p>\n<p dir=\"auto\" data-line=\"363\"><span style=\"font-weight: 400;\">We have a <a title=\"Reentrancy Examples Github Repository\" href=\"https:\/\/github.com\/Ackee-Blockchain\/reentrancy-examples\">Reentrancy Examples Github Repository<\/a>. There are other types of reentrancy attacks, as well as protocol-specific reentrancies.<br \/>\n<\/span><\/p>\n<p dir=\"auto\" data-line=\"363\">Check out our other type-specific reentrancy blogs below:<\/p>\n<ul>\n<li dir=\"auto\" data-line=\"4\"><a href=\"https:\/\/ackee.xyz\/blog\/single-function-reentrancy-attack\/\">Single Function Reentrancy Attack<\/a><\/li>\n<li dir=\"auto\" data-line=\"4\"><a href=\"https:\/\/ackee.xyz\/blog\/cross-function-reentrancy-attack\/\">Cross Function Reentrancy Attack<\/a><\/li>\n<li dir=\"auto\" data-line=\"4\"><a href=\"https:\/\/ackee.xyz\/blog\/cross-contract-reentrancy-attack\/\">Cross Contract Reentrancy Attack<\/a><\/li>\n<li dir=\"auto\" data-line=\"4\"><a href=\"https:\/\/ackee.xyz\/blog\/read-only-reentrancy-attack\/\">Read Only Reentrancy Attack<\/a><\/li>\n<li dir=\"auto\" data-line=\"4\"><a href=\"https:\/\/ackee.xyz\/blog\/cross-chain-reentrancy-attack\/\">Cross Chain Reentrancy Attack<\/a><\/li>\n<\/ul>\n<h3 dir=\"auto\" data-line=\"363\">Resources<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"367\"><a title=\"https:\/\/blog.openzeppelin.com\/exploiting-uniswap-from-reentrancy-to-actual-profit\" href=\"https:\/\/blog.openzeppelin.com\/exploiting-uniswap-from-reentrancy-to-actual-profit\" data-href=\"https:\/\/blog.openzeppelin.com\/exploiting-uniswap-from-reentrancy-to-actual-profit\">https:\/\/blog.openzeppelin.com\/exploiting-uniswap-from-reentrancy-to-actual-profit<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this blog, we describe reentrancy attacks in the ERC-777 standard. A hands-on example contracts and descriptions is here, and the executable test is here. Clone this repository and run $wake up. Then, run $wake test tests\/test_6_erc777.py to run on your local environment. The ERC-777 is a standard for fungible tokens with a transfer hook. The exchange contract allows users to exchange&hellip;<\/p>\n","protected":false},"author":24,"featured_media":1038,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,85,84,80,103],"tags":[24,86,138,64,104],"class_list":["post-759","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-exploits","category-hacks","category-solidity","category-wake","tag-ethereum","tag-hack","tag-reentrancy-attack","tag-security","tag-wake"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/04\/ERC-777-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/04\/ERC-777-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\/759","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=759"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/759\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/1038"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=759"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=759"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=759"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}