Summary
The user calls claimPrizes to collect prizes for the winner, earning some rewards in the process. However, during this process, the winner can steal the fees collected by the user without paying any gas fees.
Vulnerability Detail
The user calls claimPrizes to collect prizes for the winner, thereby earning a reward fee.
Throughout the process, the winner can set hooks before and after calling prizePool.claimPrize. The winner can set an afterClaimPrize hook and then call claimer.claimPrizes again within this afterClaimPrize hook, thereby earning reward fees without using any gas. As a result, the user can only collect the reward fee for one winner, while the remaining reward fees for other winners will all be collected by the first winner.
Consider the following scenario:
- User A calls
claimer.claimPrizesto claim prizes, setting the winners as [B, C, D, E, F] (B, C, D, E, F are all winners). - User B notices in the mempool that User A is about to call
claimer.claimPrizesto claim prizes for others. User B then callssetHooksto set theafterClaimPrizefunction, which implements a logic to loop callclaimer.claimPrizeswith winners set as [C, D, E, F]. - User A's transaction is executed, but since User B has already claimed the rewards for C, D, E, and F, User A's attempts to claim prizes for C, D, E, and F will revert. However, due to the
try-catch, User A's transaction will continue executing. In the end, User A will only receive the reward fee for User B.
This is essentially the same attack method as the previous PoolTogether vulnerability. The last audit did not completely fix it. The difference this time is that the hook uses afterClaimPrize.
Impact
The user will lose the reward fee
Code Snippet
Tool used
Manual Review
Recommendation
If the hook's purpose is for managing whitelists and blacklists, PoolTogether can create a template contract that users can manage. This way, the hook can only be set to the template contract and cannot perform additional operations.
If users are allowed to perform any operation, there is no good solution to this problem. The only options are to limit the gas, use reentrancy locks, or remove the after hook altogether.