Submitted by bart1e, also found by 0xDING99YA, BowTiedOriole, and Ward
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/VotesUpgradeable.sol#L166 
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/VotesUpgradeable.sol#L235-L244 
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/ERC721CheckpointableUpgradeable.sol#L41
If user X delegates his votes to Y, Y can block X from redelegating and even from sending his NFT anywhere, forever.
Users can acquire votes in two ways:
It is possible for them to delegate their votes to someone else. It is handled in the VotesUpgradable contract, that is derived from OpenZeppelins VotesUpgradable and the following change is made with respect to the original implementation:
It is meant to be a convenience feature so that users dont have to delegate to themselves in order to be able to vote. However, it has very serious implications.
In order to see that, lets look at the _moveDelegateVotes function that is invoked every time someone delegates his votes or wants to transfer a voting token (VerbsToken in this case as NontransferableERC20Votes is non-transferable):
As can be seen, it subtracts votes from current delegatee and adds them to the new one. There are 2 edge cases here:
If any of these conditions hold, only one of $._delegateCheckpoints is updated. This is fine in the original implementation as the function ignores cases when from == to and if function updates only $._delegateCheckpoints[from] it means that a user was delegating to 0 and when he changes delegatee, votes only should be added to some account, not subtracted from any account. Similarly, if the function updates only  $._delegateCheckpoints[to], it means that user temporarily removes his votes from the system and hence his current delegatees votes should be subtracted and not added into any other account.
As long as user cannot cause this function to update one of $._delegateCheckpoints[from] and $._delegateCheckpoints[to] several times in a row, it works correctly. It is indeed the case in the original OpenZeppelins implementation as when from == to, function doesnt perform any operation.
However, the problem with the current implementation is that it is possible to call this function with to == 0 several times in a row. In order to see it, consider the _delegate function which is called when users want to (re)delegate their votes:
As we can see, it calls _moveDelegateVotes, but with oldDelegate equal to delegates(account). But if $._delegatee[account] == address(0), that function returns account.
It means that _moveDelegateVotes can be called several times in a row with parameters (account, 0, _getVotingUnits(account)). In other words, if user delegates to address(0), he will be able to do it several times in a row as from will be different than to in _moveDelegateVotes and the function will subtract his amount of votes from his $._delegateCheckpoints every time.
It may seem that a user X who delegates to address(0) multiple times will only harm himself, but its not true as someone else can delegate to him and each time he delegates to 0, his original voting power will be subtracted from his $._delegateCheckpoints, making it 0 or some small, value. If a user Y who delegated to X wants to redelegate to someone else or transfer his tokens, _moveDelegateVotes will revert with integer underflow as it will try to subtract Ys votes from $._delegateCheckpoints[X], but it will already be either a small number or even 0 meaning that Y will be unable to transfer his tokens or redelegate.
Victims of the exploit presented above will neither be able to transfer their NFTs (the same would be true for NontransferableERC20Votes, but its not transferable by design) nor to even redelegate back to themselves or to any other address.
While it can be argued that users will only delegate to users they trust, I argue that the issue is of High severity because of the following reasons:
Please put the following test into the Voting.t.sol file and run it. It shows how a victim loses access to all his votes and all his NFTs just by delegating to someone:
VS Code
Do not allow users to delegate to address(0).
rocketman-21 (Revolution) confirmed and commented:
0xTheC0der (Judge) commented:
