31382 - [SC - High] VotingEscrowupdateUnlockTime - Its possible for...

VotingEscrow::updateUnlockTime() - It's possible for voters to update their vote tokens unlock time as many times as they wish beyond the 365 day MAX limit, violating protocol invariant.

Submitted on May 17th 2024 at 20:19:03 UTC by @OxSCSamurai for Boost | Alchemix

Report ID: #31382

Report type: Smart Contract

Report severity: High

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol

Impacts:

  • Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results

Description

Brief/Intro

VotingEscrow::updateUnlockTime() - It's possible for voters to update their vote tokens unlock time as many times as they wish beyond the 365 day MAX limit, violating protocol invariant.

  • it's made clear throughout the codebase and protocol docs that the maximum lock period for the NFT tokens is 1 year, i.e. 365 days, however, the bug in the updateUnlockTime() function makes it possible to repeatedly call this function(in different block.timestamp timestamps) to extend the unlock period almost indefinitely(e.g. extend by MAXTIME each time), but at least for almost 2 years it seems, as can be seen from the added hevm.warp() lines in the test function further down below.

Vulnerability Details

The buggy function:

Impact Details

Impact: High Likelihood: Medium Severity: High

  • a potential vote outcome manipulation impact

  • if this is indeed a bug and not just my imagination, then I've demonstrated this bug with my first PoC below

  • if you confirm that this bug is valid, I will aim to setup a PoC to demonstrate governance vote manipulation impact, which I already tried to do with my second set of PoC tests, and hopefully succeeded, but not 100% sure, please confirm?

References

https://github.com/alchemix-finance/alchemix-v2-dao/blob/9e14da88d8db05794623d8ab5f449451a10c15ac/src/VotingEscrow.sol#L709-L735

Proof of Concept

PROOF OF CONCEPT (PoC):

Modified test function:

I modified the existing protocol test for my PoC:

Test1 Result:

Main test result proving its possible to update/extend the lock period many times past the 365 day limit:

Next, added voting to the test with additional modifications:

Test function modified again:

Test results:

The below 3 tests test voting at different epochs or timestamps, as per the modified test function:

Testing voter.vote(tokenId, pools, weights, _boost1);:

Testing voter.vote(tokenId, pools, weights, _boost2);:

Testing voter.vote(tokenId, pools, weights, _boost3);:

SUGGESTED BUGFIX:

  • should take into account the previous update unlock time's block.timestamp and therefore the previous update's timestamp for after MAXTIME was added, so that we can know how much time was left to use for future unlock time updates, and save that as a state/storage variable which we will use in the updateUnlockTime() to ensure our new unlock time extension doesn't exceed the max limit of 365 days.

Last updated

Was this helpful?