code4rena-2023-06-angle-protocol-m02

[M-02] Unsafe cast in getCollateralRatio()

보고서

Summary

엣지케이스에서 캐스팅으로 인해 collatRatio 값이 절삭되어 잘못된 비율을 기반으로 계산하게 된다.

Keyword

overflow/underflow, casting

Vulnerability

    // The `stablecoinsIssued` value need to be rounded up because it is then used as a divizer when computing
    // the amount of stablecoins issued
    stablecoinsIssued = uint256(ts.normalizedStables).mulDiv(ts.normalizer, BASE_27, Math.Rounding.Up);
    if (stablecoinsIssued > 0)
        collatRatio = uint64(totalCollateralization.mulDiv(BASE_9, stablecoinsIssued, Math.Rounding.Up)); //@audit unsafe cast
    else collatRatio = type(uint64).max;

일반적으로 collatRatio 는 BASE_9 근처 값이어야 하지만, 초기에는 type(uint64).max보다 클 수도 있다.

또한 totalCollateralization는 담보물의 실제 잔액을 사용해 계산하며, stablecoinsIssued가 크지 않다면 조작될 수도 있다. stablecoinsIssued가 1 wei면 1 USD로 충분하다고 한다. collatRatio = 1 * 1e18 (=1 USD) * 1e9 / 1 wei = 1e27 이고, 1e27 > 2^64 이므로 절삭된다.

    function getCollateralRatio()
        internal
        view
        returns (
            uint64 collatRatio,
            uint256 stablecoinsIssued,
            address[] memory tokens,
            uint256[] memory balances,
            uint256[] memory subCollateralsTracker
        )
    {
        ...
        {
            ...
            for (uint256 i; i < collateralListLength; ++i) {
                ...
                uint256 collateralBalance; // Will be either the balance or the value of assets managed
                if (collateral.isManaged > 0) {
                    ...
                    (subCollateralsBalances, collateralBalance) = LibManager.totalAssets(collateral.managerData.config);
                    ...
                } else {
                    collateralBalance = IERC20(collateralList[i]).balanceOf(address(this));
                    ...
                }
                uint256 oracleValue = LibOracle.readRedemption(collateral.oracleConfig);
                totalCollateralization +=
                    (oracleValue * LibHelpers.convertDecimalTo(collateralBalance, collateral.decimals, 18)) /
                    BASE_18;
            }
        }
        
        stablecoinsIssued = uint256(ts.normalizedStables).mulDiv(ts.normalizer, BASE_27, Math.Rounding.Up);
        if (stablecoinsIssued > 0)
            collatRatio = uint64(totalCollateralization.mulDiv(BASE_9, stablecoinsIssued, Math.Rounding.Up));
        else collatRatio = type(uint64).max;
    }

이 경우 collatRatio는 uint64 캐스팅으로 인해 절삭되고, getCollateralRatio가 잘못된 비율을 반환하면 프로토콜에 심각한 영향을 끼칠 것이다.

Impact

캐스팅으로 인해 collatRatio 값이 절삭되어 잘못된 비율을 기반으로 계산하게 된다.

Mitigation

SafeCast 라이브러리를 사용하여 캐스팅으로 인해 절삭된 상황에서 거래하는 것을 방지한다.

Memo

프로젝트 측은 비현실적인 케이스라고 심각도를 낮추려 했다. 심판 측은 그럼에도 getCollateralRatio 함수가 프로토콜에서 중요한 역할을 하므로 Medium으로 쳤다.


tags: bughunting, angle protocol, smart contract, solidity, stablecoin, casting overflow underflow, severity medium