The delegate method is not protected enough by sanity checks and allows users to delegate assets to the PrizePool contract address. The TwabController contract does not keep track of the PrizePool contract address, so it's not possible to prevent the delegation.
PrizePool contract becomes eligible to be the receiver of a prize, as it's TWAB becomes different than zero. At POC#01
One attack vector would be prize loss: as it does not implement the interface to transfer prize to a different address, the prize would be locked in the contract as soon as it's claimed. At POC#02
This is especially dangerous because delegating assets to the PrizePool contract address doesn't require the target delegated account's approval nor risking the user's funds, so it's a low cost attack vector that could be used to unbalance the protocol's vaults and prizing mechanism.
It also enables further unexpected behaviour at the protocol by having addresses such as the TWABController's and Vaults set as possible draw winners.
function testPrizePoolAsPrizeReceiver()public{
contribute(100e18);
closeDraw(winningRandomNumber);
// here we are using the prize pool as the prize receiver and an already set winner address to simplify the steps taken at the test
address winner = makeAddr("winner");
mockTwab(address(this), winner, 1);
uint256 prizePoolBalance = prizeToken.balanceOf(address(prizePool));
uint256 totalWithdrawn = prizePool.totalWithdrawn();
uint96 fee = 1.13636363636363635e17;
uint256 prizeSize = prizePool.claimPrize(winner, 1, 0, address(prizePool), fee, address(0));
uint256 prizePoolBalance2 = prizeToken.balanceOf(address(prizePool));
uint256 totalWithdrawn2 = prizePool.totalWithdrawn();
assertEq(prizePoolBalance2, prizePoolBalance);
assertGt(totalWithdrawn2, totalWithdrawn);
assertEq(totalWithdrawn2, totalWithdrawn + prizeSize - fee);
}
Lines of code
https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L491-L493 https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L648-L664 https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L612-L639
Vulnerability details
Impact
The delegate method is not protected enough by sanity checks and allows users to delegate assets to the PrizePool contract address. The TwabController contract does not keep track of the PrizePool contract address, so it's not possible to prevent the delegation.
PrizePool contract becomes eligible to be the receiver of a prize, as it's TWAB becomes different than zero. At POC#01 One attack vector would be prize loss: as it does not implement the interface to transfer prize to a different address, the prize would be locked in the contract as soon as it's claimed. At POC#02 This is especially dangerous because delegating assets to the PrizePool contract address doesn't require the target delegated account's approval nor risking the user's funds, so it's a low cost attack vector that could be used to unbalance the protocol's vaults and prizing mechanism. It also enables further unexpected behaviour at the protocol by having addresses such as the TWABController's and Vaults set as possible draw winners.
Proof of Concept
POC#01
POC#02
Tools Used
Foundry and whimsical
Recommended Mitigation Steps
Assessed type
Context