code4rena-2023-06-angle-protocol-m03
[M-03] Read-only reentrancy is possible
Summary
재진입으로 인해 담보가 과도하게 잡힌 것으로 판단하게 하여 이자를 발행할 수 있다.
Keyword
reentrancy, stablecoin
Vulnerability
- contracts/transmuter/facets/Swapper.sol#L206
- contracts/transmuter/facets/Redeemer.sol#L131
- contracts/savings/SavingsVest.sol#L110
redeem과 swap 관련 로직에 재진입 가드가 없다. 따라서 일반적인 재진입 공격이 가능하다.
또한, 읽기 전용 함수에 대해서도 재진입 공격이 가능하며(재진입으로 인해 view 함수의 값이 깨진다는 의미), 이로 인해 agToken이 잘못 mint될 수 있다.
collatRatio가 BASE_9(100%) 라고 가정하자. Alice가 담보 토큰을 agToken으로 swap하려고 한다._swap함수에서, 담보 토큰을 먼저 입금하고 agToken을 mint 한다. 담보 토큰이 ERC777인 경우 훅이 실행되고 재진입이 가능하다.if (mint) { uint128 changeAmount = (amountOut.mulDiv(BASE_27, ts.normalizer, Math.Rounding.Up)).toUint128(); // The amount of stablecoins issued from a collateral are not stored as absolute variables, but // as variables normalized by a `normalizer` collatInfo.normalizedStables += uint216(changeAmount); ts.normalizedStables += changeAmount; if (permitData.length > 0) { PERMIT_2.functionCall(permitData); } else if (collatInfo.isManaged > 0) IERC20(tokenIn).safeTransferFrom( msg.sender, LibManager.transferRecipient(collatInfo.managerData.config), amountIn ); else IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); //@audit reentrancy if (collatInfo.isManaged > 0) { LibManager.invest(amountIn, collatInfo.managerData.config); } IAgToken(tokenOut).mint(to, amountOut); }- Alice가 훅을 통해
SavingsVest.accrue를 호출한다. 이 함수는 과도하게 담보가 존재하는 경우 agToken을 민팅하는 함수이다. 이 때, agToken이 아직 mint되지 않았기 때문에_transmuter.getCollateralRatio()는 잘못된 비율을 리턴할 것이다.function accrue() external returns (uint256 minted) { if (block.timestamp - lastUpdate < updateDelay && !accessControlManager.isGovernorOrGuardian(msg.sender)) revert NotAllowed(); ITransmuter _transmuter = transmuter; IAgToken _agToken = IAgToken(asset()); (uint64 collatRatio, uint256 stablecoinsIssued) = _transmuter.getCollateralRatio(); ... } getCollateralRatio는 실제보다 큰collatRatio를 리턴한다.if (stablecoinsIssued > 0) collatRatio = uint64(totalCollateralization.mulDiv(BASE_9, stablecoinsIssued, Math.Rounding.Up));SavingsVest.accrue에서collatRatio > BASE_9 + BASE_6의 조건을 맞추면 담보가 과도하게 잡혔다고 판단한다. 이 경우 이자로 agToken을 mint해준다.if (collatRatio > BASE_9 + BASE_6) { // The surplus of profit minus a fee is distributed through this contract minted = (collatRatio * stablecoinsIssued) / BASE_9 - stablecoinsIssued; // Updating normalizer in order not to double count profits _transmuter.updateNormalizer(minted, true); uint256 surplusForProtocol = (minted * protocolSafetyFee) / BASE_9; address _surplusManager = surplusManager; _surplusManager = _surplusManager == address(0) ? address(_transmuter) : _surplusManager; _agToken.mint(_surplusManager, surplusForProtocol);
Impact
담보가 과도하게 잡힌 것으로 판단하게 하여 이자를 발행한다.
Mitigation
getCollateralRatio view 함수에도 재진입 가드를 추가한다.
tags: bughunting, angle protocol, smart contract, solidity, stablecoin, reentrancy, severity medium