code4rena-2021-06-pooltogether-m05

[M-05] Actual yield source check on address will succeed for non-existent contract

보고서

Summary

로우레벨 call/delegatecall/staticcall은 해당 주소에 컨트랙트가 배포되지 않은 경우에도 true를 리턴하므로, 호출 전 컨트랙트가 해당 주소에 컨트랙트가 존재하는지를 먼저 확인해야 한다. 이를 하지 않았다고 지적했다.

Keyword

low-level call, evm, input validation

Vulnerability

로우레벨로 call/delegatecall/staticcall 을 하는 경우 해당 주소에 컨트랙트가 존재하지 않더라도 true를 리턴한다. 이에 대해 solidity docs에서도 경고하고 있으며, EVM 설계로 인한 것이다. 따라서 로우레벨 콜을 할 경우 먼저 해당 주소가 존재하는지를 직접 확인해야 한다.

YieldSourcePrizePool.initializeYieldSourcePrizePool 함수에서 로우레벨로 staticcall을 호출한다. 이 때, _yieldSource 주소에 컨트랙트가 배포되었는지 확인하지 않는다. 이 주소에 다른 컨트랙트가 배포되었다면 false를 리턴하겠지만, 이 주소가 EOA라면 true를 리턴한다.

  function initializeYieldSourcePrizePool (
    RegistryInterface _reserveRegistry,
    ControlledTokenInterface[] memory _controlledTokens,
    uint256 _maxExitFeeMantissa,
    uint256 _maxTimelockDuration,
    IYieldSource _yieldSource
  )
    public
    initializer
  {
    ...
 
    // A hack to determine whether it's an actual yield source
    (bool succeeded,) = address(_yieldSource).staticcall(abi.encode(_yieldSource.depositToken.selector));
    require(succeeded, "YieldSourcePrizePool/invalid-yield-source");
 
    emit YieldSourcePrizePoolInitialized(address(_yieldSource));
  }

Impact

컨트랙트가 잘 설정되었는지 체크하는 코드가 우회될 수 있다.

Mitigation

로우레벨 콜을 하기 전 해당 주소에 컨트랙트가 배포되어 있는지 먼저 확인한다.


tags: bughunting, pooltogether, smart contract, solidity, solidity low-level call, lack-of-input-validation-vul, severity medium