{"id":1093,"date":"2025-08-04T14:30:10","date_gmt":"2025-08-04T12:30:10","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=1093"},"modified":"2025-08-04T14:31:12","modified_gmt":"2025-08-04T12:31:12","slug":"resupply-hack-analysis","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/resupply-hack-analysis\/","title":{"rendered":"ResupplyFi Hack Analysis"},"content":{"rendered":"<div>On June 26, 2025 a single integer division flaw cost <a href=\"https:\/\/resupply.fi\/\">Resupply<\/a> $9.56M. The attacker exploited an ERC4626 &#8220;first donation&#8221; vulnerability in the cvcrvUSD vault&#8217;s ResupplyPair contract (<a href=\"https:\/\/etherscan.io\/address\/0x6e90c85a495d54c6d7e1f3400fef1f6e59f86bd6#code\">0x6e90c<\/a>). They stole $10M through in one flash loan transaction (<a href=\"https:\/\/app.blocksec.com\/explorer\/tx\/eth\/0xffbbd492e0605a8bb6d490c3cd879e87ff60862b0684160d08fd5711e7a872d3\">0xffbbd<\/a>).<\/div>\n<h2>How did this happen?<\/h2>\n<div>The ResupplyFi protocol contains a vulnerability in its handling of ERC4626 vault collateral that allows attackers to manipulate exchange rates and bypass loan-to-value (LTV) checks, resulting in unauthorized borrowing. This vulnerability was successfully exploited in a real-world attack, resulting in $9.56 million in losses just hours after the protocol&#8217;s deployment.<\/div>\n<h3>Vulnerability Details<\/h3>\n<h4>1. Exchange rate calculation vulnerability<\/h4>\n<div>\n<p><strong>Location<\/strong>: <em>ResupplyPairCore.sol:573<\/em><\/p>\n<\/div>\n<div>\n<pre><code class=\"language-solidity\">_exchangeRate = 1e36 \/ IOracle(_exchangeRateInfo.oracle).getPrices(address(collateral));<\/code><\/pre>\n<\/div>\n<div><strong>Issue<\/strong>: Integer division without rounding protection causes the exchange rate to round down to zero when the oracle price becomes extremely large.<\/div>\n<h4>2. ERC4626 donation attack vector<\/h4>\n<div><strong>Location<\/strong>: <em>ResupplyPairCore.sol:155-156<\/em><\/div>\n<div>\n<pre><code class=\"language-solidity\">underlying = IERC20(IERC4626(_collateral).asset());<\/code><\/pre>\n<\/div>\n<div>The protocol accepts ERC4626 vaults as collateral, which are vulnerable to donation attacks. In the real-world exploit, the attacker targeted the cvcrvUSD vault which was nearly empty at deployment:<\/div>\n<div>&#8211; Attacker can donate assets directly to the vault<\/div>\n<div>&#8211; This inflates the price per share dramatically<\/div>\n<div>&#8211; Oracle reports the inflated price accurately<\/div>\n<div>&#8211; Exchange rate calculation breaks: `1e36 \/ extremely_large_number = 0`<\/div>\n<h4>3. Broken solvency check<\/h4>\n<div><strong>Location<\/strong>: <em>ResupplyPairCore.sol:282<\/em><\/div>\n<div>\n<pre><code class=\"language-solidity\">uint256 _ltv = ((_borrowerAmount * _exchangeRate * LTV_PRECISION) \/ EXCHANGE_PRECISION) \/ _collateralAmount;\nreturn _ltv &lt;= _maxLTV;<\/code><\/pre>\n<\/div>\n<div>When <em>_exchangeRate = 0<\/em><\/div>\n<div>\n<p>&#8211; LTV calculation:<\/p>\n<pre><code class=\"language-solidity\">(_borrowerAmount * 0 * LTV_PRECISION) \/ EXCHANGE_PRECISION \/ _collateralAmount = 0<\/code><\/pre>\n<\/div>\n<div>\n<p>&#8211; Check: <em>0 &lt;= _maxLTV<\/em>\u00a0always <strong>returns true<\/strong><\/p>\n<p><strong>Result<\/strong>: Any amount of collateral allows unlimited borrowing<\/p>\n<\/div>\n<h3>Attack scenario<\/h3>\n<div><strong>Target<\/strong>: cvcrvUSD ERC4626 vault (nearly empty at deployment)<\/div>\n<div><\/div>\n<div>1. <strong>Initial manipulation<\/strong>:<\/div>\n<div>The attacker deposited 1 wei into the empty cvcrvUSD vault, and then made a large donation to artificially inflate `pricePerShare`.<\/div>\n<div><\/div>\n<div>2. <strong>Exchange rate corruption<\/strong>:<\/div>\n<div>The attacker:<\/div>\n<div>&#8211; Called <em>borrow()<\/em> on newly deployed ResupplyPair<\/div>\n<div>&#8211; Triggered oracle price fetch: <em>getPrices(address(collateral))<\/em><\/div>\n<div>&#8211; Price extremely high due to donation inflation<\/div>\n<div>&#8211;<em>_exchangeRate = 1e36 \/ price<\/em>\u00a0computed to zero via Solidity floor division<\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2025\/07\/issue-scaled.png\" width=\"1337\" height=\"926\" \/><\/div>\n<div><\/div>\n<div>3. <strong>Solvency bypass<\/strong>:<\/div>\n<div>&#8211; <em>_isSolvent()<\/em> check used corrupted <em>_exchangeRate = 0<\/em><\/div>\n<div>\n<p>&#8211; LTV calculation:<\/p>\n<pre><code class=\"language-solidity\">(_borrowAmount * 0 * LTV_PRECISION) \/ EXCHANGE_PRECISION \/ _collateralAmount = 0<\/code><\/pre>\n<\/div>\n<div>\n<p>&#8211; Check <em>0 &lt;= _maxLTV<\/em> always returns true<\/p>\n<\/div>\n<div>4. <strong>Mass borrowing<\/strong>:<\/div>\n<div><\/div>\n<div>The attacker borrowed $10 million worth of reUSD using just 1 wei of collateral, and swapped and redistributed the stolen funds. This led to a <strong>final profit of $9.56 million<\/strong> split across multiple addresses.<\/div>\n<h4>Generic attack pattern<\/h4>\n<div>1. Target newly deployed or low-liquidity ERC4626 vault<\/div>\n<div>2. Donate large amount of underlying assets to inflate share price<\/div>\n<div>3. Mint minimal vault shares (1 wei)<\/div>\n<div>4. Oracle price inflates to astronomical levels<\/div>\n<div>5. Exchange rate rounds to zero due to integer division<\/div>\n<div>6. Deposit minimal collateral and bypass LTV checks<\/div>\n<div>7. Borrow maximum available funds<\/div>\n<h3>Recommendations<\/h3>\n<h4>\u00a0Immediate mitigations<\/h4>\n<div>1. <strong>Add an exchange rate floor<\/strong><\/div>\n<div>\n<pre><code class=\"language-solidity\">_exchangeRate = 1e36 \/ IOracle(_exchangeRateInfo.oracle).getPrices(address(collateral));\nrequire(_exchangeRate &gt; 0, &quot;Invalid exchange rate&quot;);\n_exchangeRate = _exchangeRate == 0 ? 1 : _exchangeRate;<\/code><\/pre>\n<\/div>\n<div>\n<p>2. <strong>Add minimum collateral requirements<\/strong><\/p>\n<\/div>\n<div>Enforce minimum deposit amounts for ERC4626 vaults and implement share\/asset ratio sanity checks.<\/div>\n<h2>References<\/h2>\n<div>&#8211; ERC4626 Standard: https:\/\/eips.ethereum.org\/EIPS\/eip-4626<\/div>\n<div>&#8211; ResupplyFi Official Response: https:\/\/x.com\/ResupplyFi\/status\/1938092252431036491<\/div>\n<div>&#8211; Target Vault: <strong>cvcrvUSD ERC4626 vault<\/strong><\/div>\n<div><\/div>\n<div>\n<hr \/>\n<\/div>\n<div><\/div>\n<div>Want to learn more? Read our recent <a href=\"https:\/\/ackee.xyz\/blog\/gmx-hack-analysis-attack-scenarios-with-wake\/\">blog covering the 2025 GMX hack<\/a>.<\/div>\n<div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>On June 26, 2025 a single integer division flaw cost Resupply $9.56M. The attacker exploited an ERC4626 &#8220;first donation&#8221; vulnerability in the cvcrvUSD vault&#8217;s ResupplyPair contract (0x6e90c). They stole $10M through in one flash loan transaction (0xffbbd). How did this happen? The ResupplyFi protocol contains a vulnerability in its handling of ERC4626 vault collateral that allows attackers to manipulate exchange rates and&hellip;<\/p>\n","protected":false},"author":25,"featured_media":1016,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,10,85,84],"tags":[24,14,86,64],"class_list":["post-1093","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-ethereum","category-exploits","category-hacks","tag-ethereum","tag-exploit","tag-hack","tag-security"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/03\/Cross-Function-Reentrancy-Attack-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/03\/Cross-Function-Reentrancy-Attack-600x600.png","author_info":{"display_name":"Dima Khimchenko","author_link":"https:\/\/ackee.xyz\/blog\/author\/dima-khimchenko\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1093","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\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=1093"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1093\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/1016"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=1093"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=1093"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=1093"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}