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