code4rena-2021-06-pooltogether-h04
[H-04] withdraw timelock can be circumvented
Summary
withdrawWithTimelockFrom를 중복으로 호출할 시 Timelock을 덮어쓴다. 다량의 토큰을 출금 요청한 뒤 0만큼 출금 요청하면 Timelock 종료 시간이 현재로 덮어써져 바로 출금이 가능하다.
Keyword
bug, logic flaw, timelock
Vulnerability
PrizePool에서 Timelock을 우회하여 출금이 가능하다. withdrawWithTimelockFrom 호출 시 기존 Timelock을 덮어쓴다. 따라서 먼저 다량의 토큰을 출금 요청하고, 다시 소량 또는 0만큼 출금 요청을 하면 Timelock이 덮어써진다. 따라서 기한이 다 끝날 때까지 기다리지 않고 토큰을 출금해갈 수 있다.
function withdrawWithTimelockFrom(
address from,
uint256 amount,
address controlledToken
)
external override
nonReentrant
onlyControlledToken(controlledToken)
returns (uint256)
{
uint256 blockTime = _currentTime();
(uint256 lockDuration, uint256 burnedCredit) = _calculateTimelockDuration(from, controlledToken, amount);
uint256 unlockTimestamp = blockTime.add(lockDuration);
_burnCredit(from, controlledToken, burnedCredit);
ControlledToken(controlledToken).controllerBurnFrom(_msgSender(), from, amount);
_mintTimelock(from, amount, unlockTimestamp);
emit TimelockedWithdrawal(_msgSender(), from, controlledToken, amount, unlockTimestamp);
// return the block at which the funds will be available
return unlockTimestamp;
}
function _mintTimelock(address user, uint256 amount, uint256 timestamp) internal {
// Sweep the old balance, if any
address[] memory users = new address[](1);
users[0] = user;
_sweepTimelockBalances(users);
timelockTotalSupply = timelockTotalSupply.add(amount);
_timelockBalances[user] = _timelockBalances[user].add(amount);
_unlockTimestamps[user] = timestamp;
// if the funds should already be unlocked
if (timestamp <= _currentTime()) {
_sweepTimelockBalances(users);
}
}withdrawWithTimelockFrom(user, amount=userBalance)를 호출하여 다량의 토큰에 Timelock을 건다.withdrawWithTimelockFrom(user, amount=0)를 호출하여 0만큼 출금 신청을 한다. 이렇게 하면lockDuration이 0이 되고,unlockTimestamp = blockTime.add(0);이 되어 현재 시간이 된다._mintTimelock함수에서_unlockTimestamps[user] = timestamp;로 덮어써져 Timelock이 해제된다.sweepTimelockBalances함수를 호출해 토큰을 바로 꺼내간다.
Impact
패널티 없이 출금이 가능하다. 공격자가 복권 추첨 직전에 참여했다가 패널티 없이 바로 나올 수 있으므로 프로토콜에 심각한 이슈이다.
Mitigation
withdrawWithTimelockFrom 중복 호출을 막았다. withdrawWithTimelockBalance require timelockBalance gt zerp
tags: bughunting, pooltogether, smart contract, solidity, logic flaw, timelock, severity high