msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: PeriodicPrizeStrategy - RNG failure can lock user funds
Severity: high

Description:
To prevent manipulation of the `SortitionSumTree` after a requested random number enters the mempool, users are unable to withdraw funds while the strategy contract waits on a random number request between execution of `startAward()` and `completeAward()`.\\nIf an rng request fails, however, there is no way to exit this locked state. After an rng request times out, only `startAward()` can be called, which will make another rng request and re-enter the same locked state. The rng provider can also not be updated while the contract is in this state. If the rng provider fails permanently, user funds are permanently locked.\\n`requireNotLocked()` prevents transfers, deposits, or withdrawals when there is a pending award.\\n```\\nfunction beforeTokenTransfer(address from, address to, uint256 amount, address controlledToken) external override onlyPrizePool {\\n  if (controlledToken == address(ticket)) {\\n    \\_requireNotLocked();\\n  }\\n```\\n\\n```\\nfunction \\_requireNotLocked() internal view {\\n  uint256 currentBlock = \\_currentBlock();\\n  require(rngRequest.lockBlock == 0 || currentBlock < rngRequest.lockBlock, "PeriodicPrizeStrategy/rng-in-flight");\\n}\\n```\\n\\n`setRngService()` reverts if there is a pending or timed-out rng request\\n```\\nfunction setRngService(RNGInterface rngService) external onlyOwner {\\n  require(!isRngRequested(), "PeriodicPrizeStrategy/rng-in-flight");\\n```\\n

Impact:

Recommended mitigation:
Instead of forcing the pending award phase to be re-entered in the event of an rng request time-out, provide an `exitAwardPhase()` function that ends the award phase without paying out the award. This will at least allow users to withdraw their funds in the event of a catastrophic failure of the rng service. It may also be prudent to allow the rng service to be updated in the event of an rng request time out.
Vulnerability: LootBox - Unprotected selfdestruct in proxy implementation
Severity: high

Description:
When the `LootBoxController` is deployed, it also deploys an instance of `LootBox`. When someone calls `LootBoxController.plunder()` or `LootBoxController.executeCall()` the controller actually deploys a temporary proxy contract to a deterministic address using `create2`, then calls out to it to collect the loot.\\nThe `LootBox` implementation contract is completely unprotected, exposing all its functionality to any actor on the blockchain. The most critical functionality is actually the `LootBox.destroy()` method that calls `selfdestruct()` on the implementation contract.\\nTherefore, an unauthenticated user can `selfdestruct` the `LootBox` proxy implementation and cause the complete system to become dysfunctional. As an effect, none of the AirDrops that were delivered based on this contract will be redeemable (Note: `create2` deploy address is calculated from the current contract address and salt). Funds may be lost.\\n```\\nconstructor () public {\\n  lootBoxActionInstance = new LootBox();\\n  lootBoxActionBytecode = MinimalProxyLibrary.minimalProxy(address(lootBoxActionInstance));\\n}\\n```\\n\\n```\\n/// @notice Destroys this contract using `selfdestruct`\\n/// @param to The address to send remaining Ether to\\nfunction destroy(address payable to) external {\\n  selfdestruct(to);\\n}\\n```\\n\\nnot in scope but listed for completeness\\n```\\ncontract CounterfactualAction {\\n  function depositTo(address payable user, PrizePool prizePool, address output, address referrer) external {\\n    IERC20 token = IERC20(prizePool.token());\\n    uint256 amount = token.balanceOf(address(this));\\n    token.approve(address(prizePool), amount);\\n    prizePool.depositTo(user, amount, output, referrer);\\n    selfdestruct(user);\\n  }\\n\\n  function cancel(address payable user, PrizePool prizePool) external {\\n    IERC20 token = IERC20(prizePool.token());\\n    token.transfer(user, token.balanceOf(address(this)));\\n    selfdestruct(user);\\n  }\\n```\\n

Impact:

Recommended mitigation:
Enforce that only the deployer of the contract can call functionality in the contract. Make sure that nobody can destroy the implementation of proxy contracts.
Vulnerability: PeriodicPriceStrategy - trustedForwarder can impersonate any msg.sender
Severity: high

Description:
The `trustedForwarder` undermines the trust assumptions in the system. For example, one would assume that the access control modifier `onlyPrizePool` would only allow the configured `PrizePool` to call certain methods. However, in reality, the `trustedForwarder` can assume this position as well. The same is true for the `onlyOwnerOrListener` modifier. One would assume `msg.sender` must either be `periodicPrizeStrategyListener` or `owner` (the initial deployer) while the `trustedForwarder` can assume any of the administrative roles.\\nThe centralization of power to allow one account to impersonate other components and roles (owner, `listener`, prizePool) in the system is a concern by itself and may give users pause when deciding whether to trust the contract system. The fact that the `trustedForwarder` can spoof events for any `msg.sender` may also make it hard to keep an accurate log trail of events in case of a security incident.\\nNote: The same functionality seems to be used in `ControlledToken` and other contracts which allows the `trustedForwarder` to assume any tokenholder in `ERC20UpgradeSafe`. There is practically no guarantee to `ControlledToken` holders.\\nNote: The trustedForwarder/msgSender() pattern is used in multiple contracts, many of which are not in the scope of this assessment.\\naccess control modifiers that can be impersonated\\n```\\nmodifier onlyPrizePool() {\\n  require(\\_msgSender() == address(prizePool), "PeriodicPrizeStrategy/only-prize-pool");\\n  \\_;\\n}\\n```\\n\\n```\\nmodifier onlyOwnerOrListener() {\\n  require(\\_msgSender() == owner() || \\_msgSender() == address(periodicPrizeStrategyListener), "PeriodicPrizeStrategy/only-owner-or-listener");\\n  \\_;\\n}\\n```\\n\\nevent `msg.sender` that can be spoofed because the actual `msg.sender` can be `trustedForwarder`\\n```\\nemit PrizePoolOpened(\\_msgSender(), prizePeriodStartedAt);\\n```\\n\\n```\\nemit PrizePoolAwardStarted(\\_msgSender(), address(prizePool), requestId, lockBlock);\\n```\\n\\n```\\nemit PrizePoolAwarded(\\_msgSender(), randomNumber);\\nemit PrizePoolOpened(\\_msgSender(), prizePeriodStartedAt);\\n```\\n\\n`_msgSender()` implementation allows the `trustedForwarder` to impersonate any `msg.sender` address\\n```\\n/// @dev Provides information about the current execution context for GSN Meta-Txs.\\n/// @return The payable address of the message sender\\nfunction \\_msgSender()\\n  internal\\n  override(BaseRelayRecipient, ContextUpgradeSafe)\\n  virtual\\n  view\\n  returns (address payable)\\n{\\n  return BaseRelayRecipient.\\_msgSender();\\n}\\n```\\n

Impact:

Recommended mitigation:
Remove the `trustedForwarder` or restrict the type of actions the forwarder can perform and don't allow it to impersonate other components in the system. Make sure users understand the trust assumptions and who has what powers in the system. Make sure to keep an accurate log trail of who performed which action on whom's behalf.
Vulnerability: PeriodicPrizeStrategy - Token with callback related warnings (ERC777 a.o.)
Severity: medium

Description:
This issue is highly dependent on the configuration of the system. If an admin decides to allow callback enabled token (e.g. `ERC20` compliant `ERC777` or other ERC721/ERC20 extensions) as awards then one recipient may be able to\\nblock the payout for everyone by forcing a revert in the callback when accepting token awards\\nuse the callback to siphon gas, mint gas token, or similar activities\\npotentially re-enter the `PrizeStrategy` contract in an attempt to manipulate the payout (e.g. by immediately withdrawing from the pool to manipulate the 2nd ticket.draw())\\n```\\nfunction \\_awardExternalErc721s(address winner) internal {\\n  address currentToken = externalErc721s.start();\\n  while (currentToken != address(0) && currentToken != externalErc721s.end()) {\\n    uint256 balance = IERC721(currentToken).balanceOf(address(prizePool));\\n    if (balance > 0) {\\n      prizePool.awardExternalERC721(winner, currentToken, externalErc721TokenIds[currentToken]);\\n      delete externalErc721TokenIds[currentToken];\\n    }\\n    currentToken = externalErc721s.next(currentToken);\\n  }\\n  externalErc721s.clearAll();\\n}\\n```\\n

Impact:

Recommended mitigation:
It is highly recommended to not allow tokens with callback functionality into the system. Document and/or implement safeguards that disallow the use of callback enabled tokens. Consider implementing means for the “other winners” to withdraw their share of the rewards independently from others.
Vulnerability: PeriodicPrizeStrategy - unbounded external tokens linked list may be used to force a gas DoS
Severity: medium

Description:
The size of the linked list of ERC20/ERC721 token awards is not limited. This fact may be exploited by an administrative account by adding an excessive number of external token addresses.\\nThe winning user might want to claim their win by calling `completeAward()` which fails in one of the `_distribute() -> _awardAllExternalTokens() -> _awardExternalErc20s/_awardExternalErc721s` while loops if too many token addresses are configured and gas consumption hits the block gas limit (or it just gets too expensive for the user to call).\\nNote: an admin can recover from this situation by removing items from the list.\\n```\\n/// @notice Adds an external ERC20 token type as an additional prize that can be awarded\\n/// @dev Only the Prize-Strategy owner/creator can assign external tokens,\\n/// and they must be approved by the Prize-Pool\\n/// @param \\_externalErc20 The address of an ERC20 token to be awarded\\nfunction addExternalErc20Award(address \\_externalErc20) external onlyOwnerOrListener {\\n  \\_addExternalErc20Award(\\_externalErc20);\\n}\\n\\nfunction \\_addExternalErc20Award(address \\_externalErc20) internal {\\n  require(prizePool.canAwardExternal(\\_externalErc20), "PeriodicPrizeStrategy/cannot-award-external");\\n  externalErc20s.addAddress(\\_externalErc20);\\n  emit ExternalErc20AwardAdded(\\_externalErc20);\\n}\\n```\\n\\n```\\n/// @param newAddress The address to shift to the front of the list\\nfunction addAddress(Mapping storage self, address newAddress) internal {\\n  require(newAddress != SENTINEL && newAddress != address(0), "Invalid address");\\n  require(self.addressMap[newAddress] == address(0), "Already added");\\n  self.addressMap[newAddress] = self.addressMap[SENTINEL];\\n  self.addressMap[SENTINEL] = newAddress;\\n  self.count = self.count + 1;\\n}\\n```\\n\\nawarding the tokens loops through the linked list of configured tokens\\n```\\n/// @notice Awards all external ERC721 tokens to the given user.\\n/// The external tokens must be held by the PrizePool contract.\\n/// @dev The list of ERC721s is reset after every award\\n/// @param winner The user to transfer the tokens to\\nfunction \\_awardExternalErc721s(address winner) internal {\\n  address currentToken = externalErc721s.start();\\n  while (currentToken != address(0) && currentToken != externalErc721s.end()) {\\n    uint256 balance = IERC721(currentToken).balanceOf(address(prizePool));\\n    if (balance > 0) {\\n      prizePool.awardExternalERC721(winner, currentToken, externalErc721TokenIds[currentToken]);\\n      delete externalErc721TokenIds[currentToken];\\n    }\\n    currentToken = externalErc721s.next(currentToken);\\n  }\\n  externalErc721s.clearAll();\\n}\\n```\\n

Impact:

Recommended mitigation:
Limit the number of tokens an admin can add. Consider implementing an interface that allows the user to claim tokens one-by-one or in user-configured batches.
Vulnerability: MultipleWinners - setNumberOfWinners does not enforce count>0
Severity: medium

Description:
The constructor of `MultipleWinners` enforces that the argument `_numberOfWinners > 0` while `setNumberOfWinners` does not. A careless or malicious admin might set `__numberOfWinners` to zero to cause the `distribute()` method to throw and not pay out any winners.\\nenforced in the constructor\\n```\\nrequire(\\_numberOfWinners > 0, "MultipleWinners/num-gt-zero");\\n```\\n\\nnot enforced when updating the value at a later stage\\n```\\nfunction setNumberOfWinners(uint256 count) external onlyOwner {\\n  \\_\\_numberOfWinners = count;\\n\\n  emit NumberOfWinnersSet(count);\\n}\\n```\\n

Impact:

Recommended mitigation:
Require that `numberOfWinners > 0`.
Vulnerability: LootBox - plunder should disallow plundering to address(0)
Severity: medium

Description:
Anyone can call `LootboxController.plunder()` to plunder on behalf of a `tokenId` owner. If a `LootBox` received an AirDrop but no `NFT` was issued to an owner (yet) this might open up an opportunity for a malicious actor to call `plunder()` in an attempt to burn the ETH and any airdropped tokens that allow transfers to `address(0)`.\\nNote:\\nDepending on the token implementation, transfers may or may not revert if the `toAddress == address(0)`, while burning the `ETH` will succeed.\\nThis might allow anyone to forcefully burn received `ETH` that would otherwise be available to the future beneficiary\\nIf the airdrop and transfer of `LootBox` ownership are not done within one transaction, this might open up a front-running window that allows a third party to burn air-dropped `ETH` before it can be claimed by the `owner`.\\nconsider one component issues the airdrop in one transaction (or block) and setting the `owner` in a later transaction (or block). The `owner` is unset for a short duration of time which might allow anyone to burn `ETH` held by the `LootBox` proxy instance.\\n`plunder()` receiving the `owner` of an `ERC721.tokenId`\\n```\\nfunction plunder(\\n  address erc721,\\n  uint256 tokenId,\\n  IERC20[] calldata erc20s,\\n  LootBox.WithdrawERC721[] calldata erc721s,\\n  LootBox.WithdrawERC1155[] calldata erc1155s\\n) external {\\n  address payable owner = payable(IERC721(erc721).ownerOf(tokenId));\\n```\\n\\nThe modified `ERC721` returns `address(0)` if the owner is not known\\n```\\n \\* @dev See {IERC721-ownerOf}.\\n \\*/\\nfunction ownerOf(uint256 tokenId) public view override returns (address) {\\n    return \\_tokenOwners[tokenId];\\n}\\n```\\n\\nWhile `withdraw[ERC20|ERC721|ERC1155]` fail with `to == address(0)`, `transferEther()` succeeds and burns the eth by sending it to `address(0)`\\n```\\nfunction plunder(\\n  IERC20[] memory erc20,\\n  WithdrawERC721[] memory erc721,\\n  WithdrawERC1155[] memory erc1155,\\n  address payable to\\n) external {\\n  \\_withdrawERC20(erc20, to);\\n  \\_withdrawERC721(erc721, to);\\n  \\_withdrawERC1155(erc1155, to);\\n  transferEther(to, address(this).balance);\\n}\\n```\\n

Impact:

Recommended mitigation:
Require that the destination address `to` in `plunder()` and `transferEther()` is not `address(0)`.
Vulnerability: PeriodicPrizeStrategy - Inconsistent behavior between award-phase modifiers and view functions
Severity: low

Description:
The logic in the `canStartAward()` function is inconsistent with that of the `requireCanStartAward` modifier, and the logic in the `canCompleteAward()` function is inconsistent with that of the `requireCanCompleteAward` modifier. Neither of these view functions appear to be used elsewhere in the codebase, but the similarities between the function names and the corresponding modifiers is highly misleading.\\n`canStartAward()` is inconsistent with `requireCanStartAward`\\n```\\nfunction canStartAward() external view returns (bool) {\\n  return \\_isPrizePeriodOver() && !isRngRequested();\\n}\\n```\\n\\n```\\nmodifier requireCanStartAward() {\\n  require(\\_isPrizePeriodOver(), "PeriodicPrizeStrategy/prize-period-not-over");\\n  require(!isRngRequested() || isRngTimedOut(), "PeriodicPrizeStrategy/rng-already-requested");\\n  \\_;\\n}\\n```\\n\\n`canCompleteAward()` is inconsistent with `requireCanCompleteAward`\\n```\\nfunction canCompleteAward() external view returns (bool) {\\n  return isRngRequested() && isRngCompleted();\\n}\\n```\\n\\n```\\nmodifier requireCanCompleteAward() {\\n  require(\\_isPrizePeriodOver(), "PeriodicPrizeStrategy/prize-period-not-over");\\n  require(isRngRequested(), "PeriodicPrizeStrategy/rng-not-requested");\\n  require(isRngCompleted(), "PeriodicPrizeStrategy/rng-not-complete");\\n  \\_;\\n}\\n```\\n

Impact:

Recommended mitigation:
Make the logic consistent between the view functions and the modifiers of the same name or remove the functions.
Vulnerability: MultipleWinners - Awards can be guaranteed with a set number of tickets
Severity: low

Description:
Because additional award drawings are distributed at a constant interval in the `SortitionSumTree` by `MultipleWinners._distribute()`, any user that holds a number of tickets `>= floor(totalSupply / __numberOfWinners)` can guarantee at least one award regardless of the initial drawing.\\nMultipleWinners._distribute():\\n```\\nuint256 ticketSplit = totalSupply.div(\\_\\_numberOfWinners);\\nuint256 nextRandom = randomNumber.add(ticketSplit);\\n// the other winners receive their prizeShares\\nfor (uint256 winnerCount = 1; winnerCount < \\_\\_numberOfWinners; winnerCount++) {\\n  winners[winnerCount] = ticket.draw(nextRandom);\\n  nextRandom = nextRandom.add(ticketSplit);\\n}\\n```\\n

Impact:

Recommended mitigation:
Do not distribute awards at fixed intervals from the initial drawing, but instead randomize the additional drawings as well.
Vulnerability: MultipleWinners - Inconsistent behavior compared to SingleRandomWinner
Severity: low

Description:
The `MultipleWinners` strategy carries out award distribution to the zero address if `ticket.draw()` returns `address(0)` (indicating an error condition) while `SingleRandomWinner` does not.\\n`SingleRandomWinner` silently skips award distribution if `ticket.draw()` returns `address(0)`.\\n```\\ncontract SingleRandomWinner is PeriodicPrizeStrategy {\\n  function \\_distribute(uint256 randomNumber) internal override {\\n    uint256 prize = prizePool.captureAwardBalance();\\n    address winner = ticket.draw(randomNumber);\\n    if (winner != address(0)) {\\n      \\_awardTickets(winner, prize);\\n      \\_awardAllExternalTokens(winner);\\n    }\\n  }\\n}\\n```\\n\\n`MultipleWinners` still attempts to distribute awards if `ticket.draw()` returns `address(0)`. This may or may not succeed depending on the implementation of the tokens included in the `externalErc20s` and `externalErc721s` linked lists.\\n```\\nfunction \\_distribute(uint256 randomNumber) internal override {\\n  uint256 prize = prizePool.captureAwardBalance();\\n\\n  // main winner gets all external tokens\\n  address mainWinner = ticket.draw(randomNumber);\\n  \\_awardAllExternalTokens(mainWinner);\\n\\n  address[] memory winners = new address[](\\_\\_numberOfWinners);\\n  winners[0] = mainWinner;\\n```\\n

Impact:

Recommended mitigation:
Implement consistent behavior. Avoid hiding error conditions and consider throwing an exception instead.
Vulnerability: Initialize implementations for proxy contracts and protect initialization methods
Severity: low

Description:
Any situation where the implementation of proxy contracts can be initialized by third parties should be avoided. This can be the case if the `initialize` function is unprotected or not initialized immediately after deployment. Since the implementation contract is not meant to be used directly without a proxy delegate-calling to it, it is recommended to protect the initialization method of the implementation by initializing on deployment.\\nThis affects all proxy implementations (the delegatecall target contract) deployed in the system.\\nThe implementation for `MultipleWinners` is not initialized. Even though not directly used by the system it may be initialized by a third party.\\n```\\nconstructor () public {\\n  instance = new MultipleWinners();\\n}\\n```\\n\\nThe deployed `ERC721Contract` is not initialized.\\n```\\nconstructor () public {\\n  erc721ControlledInstance = new ERC721Controlled();\\n  erc721ControlledBytecode = MinimalProxyLibrary.minimalProxy(address(erc721ControlledInstance));\\n}\\n```\\n\\nThe deployed `LootBox` is not initialized.\\n```\\nconstructor () public {\\n  lootBoxActionInstance = new LootBox();\\n  lootBoxActionBytecode = MinimalProxyLibrary.minimalProxy(address(lootBoxActionInstance));\\n}\\n```\\n

Impact:

Recommended mitigation:
Initialize unprotected implementation contracts in the implementation's constructor. Protect initialization methods from being called by unauthorized parties or ensure that deployment of the proxy and initialization is performed in the same transaction.
Vulnerability: LootBox - transferEther should be internal
Severity: low

Description:
`LootBox.transferEther()` can be `internal` as it is only called from `LootBox.plunder()` and the LootBox(proxy) instances are generally very short-living (created and destroyed within one transaction).\\n```\\nfunction transferEther(address payable to, uint256 amount) public {\\n  to.transfer(amount);\\n\\n  emit TransferredEther(to, amount);\\n}\\n```\\n

Impact:

Recommended mitigation:
Restrict transferEther()'s visibility to `internal`.
Vulnerability: LootBox - executeCalls can be misused to relay calls
Severity: low

Description:
`LootBox` is deployed with `LootBoxController` and serves as the implementation for individual `create2` lootbox proxy contracts. None of the methods of the `LootBox` implementation contract are access restricted. A malicious actor may therefore use the `executeCalls()` method to relay arbitrary calls to other contracts on the blockchain in an attempt to disguise the origin or misuse the reputation of the `LootBox` contract (as it belongs to the PoolTogether project).\\nNote: allows non-value and value calls (deposits can be forces via selfdestruct)\\n```\\nfunction executeCalls(Call[] calldata calls) external returns (bytes[] memory) {\\n  bytes[] memory response = new bytes[](calls.length);\\n  for (uint256 i = 0; i < calls.length; i++) {\\n    response[i] = \\_executeCall(calls[i].to, calls[i].value, calls[i].data);\\n  }\\n  return response;\\n}\\n```\\n

Impact:

Recommended mitigation:
Restrict access to call forwarding functionality to trusted entities. Consider implementing the `Ownable` pattern allowing access to functionality to the owner only.
Vulnerability: ERC20 tokens with no return value will fail to transfer
Severity: high

Description:
Although the ERC20 standard suggests that a transfer should return `true` on success, many tokens are non-compliant in this regard.\\nIn that case, the `.transfer()` call here will revert even if the transfer is successful, because solidity will check that the RETURNDATASIZE matches the ERC20 interface.\\n```\\nif (!instance.transfer(getSendAddress(), forwarderBalance)) {\\n    revert('Could not gather ERC20');\\n}\\n```\\n

Impact:

Recommended mitigation:
Consider using OpenZeppelin's SafeERC20.
Vulnerability: Delegated transactions can be executed for multiple accounts
Severity: high

Description:
The `Gateway` contract allows users to create meta transactions triggered by the system's backend. To do so, one of the owners of the account should sign the message in the following format:\\n```\\naddress sender = \\_hashPrimaryTypedData(\\n \\_hashTypedData(\\n nonce,\\n to,\\n data\\n )\\n).recoverAddress(senderSignature);\\n```\\n\\nThe message includes a nonce, destination address, and call data. The problem is that this message does not include the `account` address. So if the `sender` is the owner of multiple accounts, this meta transaction can be called for multiple accounts.

Impact:

Recommended mitigation:
Resolution\\nComment from the client: The issue has been solved\\nAdd the `account` field in the signed message or make sure that any address can be the owner of only one `account`.