Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calls to get_virtual_price() are vulnerable to read-only reentrancy #376

Open
code423n4 opened this issue Dec 20, 2023 · 0 comments
Open

Comments

@code423n4
Copy link
Contributor

Lines of code


117

Vulnerability details


get_virtual_price() was originally considered to be a manipulation-resistant price - suitable as a price oracle, but it was later found to be vulnerable to a read-only reentrancy attack, where the Curve contract could be put into a partially-modified state, and an attacker could gain control via the raw external call remove_liquidity() makes. The attacker could use this to artificially inflate the price of the LP token/its balance, and use the inflated balance to take out loans which become undercollateralized at the end of the transaction, or to buy assets at exchange rates not actually available on the open market. In order to protect against the attack, many protocols call uint256[2] calldata amts; ICurvePool(token).remove_liquidity(0, amts); prior to calling get_virtual_price() since calling remove_liquidity() will ensure, via a reentrancy guard, that the user isn't currently manipulating the value, and since amounts are zero, it has no other effect. Another alternative is to call v1 pool.claim_admin_fees() or to call v2 ICurveOwner(pool.owner()).withdraw_admin_fees(address(pool)).

File: contracts/oracle/implementations/ARBTriCryptoOracle.sol

117      function _get() internal view returns (uint256 _maxPrice) {
118          uint256 _vp = TRI_CRYPTO.get_virtual_price();
119  
120          // Get the prices from chainlink and add 10 decimals
121          uint256 _btcPrice = uint256(BTC_FEED.latestAnswer()) * 1e10;
122          uint256 _wbtcPrice = uint256(WBTC_FEED.latestAnswer()) * 1e10;
123          uint256 _ethPrice = uint256(ETH_FEED.latestAnswer()) * 1e10;
124          uint256 _usdtPrice = uint256(USDT_FEED.latestAnswer()) * 1e10;
125  
126          uint256 _minWbtcPrice = (_wbtcPrice < 1e18)
127              ? (_wbtcPrice * _btcPrice) / 1e18
128              : _btcPrice;
129  
130          uint256 _basePrices = (_minWbtcPrice * _ethPrice * _usdtPrice);
131  
132          _maxPrice = (3 * _vp * FixedPointMathLib.cbrt(_basePrices)) / 1 ether;
133  
134          // ((A/A0) * (gamma/gamma0)**2) ** (1/3)
135          uint256 _g = (TRI_CRYPTO.gamma() * 1 ether) / GAMMA0;
136          uint256 _a = (TRI_CRYPTO.A() * 1 ether) / A0;
137          uint256 _discount = Math.max((_g ** 2 / 1 ether) * _a, 1e34); // handle qbrt nonconvergence
138          // if discount is small, we take an upper bound
139          _discount = (FixedPointMathLib.sqrt(_discount) * DISCOUNT0) / 1 ether;
140  
141          _maxPrice -= (_maxPrice * _discount) / 1 ether;
142:     }

Assessed type


other

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant