Summary
The function retrieves the poolRegistryEntry using QuantAMMWeightedPool(_poolAddress).poolRegistry(), but it does not validate whether the _poolAddress corresponds to a legitimate, pre-approved pool in the QuantAMM system. an attacker may supply a malicious or manipulated _poolAddress, the mask checks (MASK_POOL_OWNER_UPDATES, MASK_POOL_QUANTAMM_ADMIN_UPDATES, etc.) may inadvertently grant unauthorized access to weight updates..
Vulnerability Details
function setWeightsManually(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible,
uint _numberOfAssets
) external {
uint256 poolRegistryEntry = QuantAMMWeightedPool(_poolAddress).poolRegistry();
if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
} else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
require(msg.sender == quantammAdmin, "ONLYADMIN");
} else {
revert("No permission to set weight values");
}
for (uint i; i < _weights.length; i++) {
if (i < _numberOfAssets) {
require(_weights[i] > 0, "Negative weight not allowed");
require(_weights[i] < 1e18, "greater than 1 weight not allowed");
}
}
IQuantAMMWeightedPool(_poolAddress).setWeights(_weights, _poolAddress, _lastInterpolationTimePossible);
emit SetWeightManual(msg.sender, _poolAddress, _weights, _lastInterpolationTimePossible);
}
https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/UpdateWeightRunner.sol#L559-L594
Impact
Malicious actors could create pools with spoofed or manipulated registry entries to bypass access control.
unauthorized users could potentially bypass the permission checks by using a pool with manipulated registry values.
Allowing unauthorized updates to weights could destabilize the pool and compromise the protocol’s security and reliability.
Tools Used
Recommendations
Maintain a Registry of Approved Pools: Introduce a mapping(address => bool) to track approved pool addresses and ensure _poolAddress is validated against this list before proceeding.
mapping(address => bool) public approvedPools;
function approvePool(address _poolAddress) external onlyOwner {
require(_poolAddress != address(0), "Invalid pool address");
approvedPools[_poolAddress] = true;
}
function revokePool(address _poolAddress) external onlyOwner {
require(approvedPools[_poolAddress], "Pool not approved");
approvedPools[_poolAddress] = false;
}
Validate _poolAddress in setWeightsManually: Before proceeding with registry mask checks, ensure _poolAddress is part of the approved pools.
require(approvedPools[_poolAddress], "Pool address not approved");
**setWeightsManually **Here’s a corrected implementation:
function setWeightsManually(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible,
uint _numberOfAssets
) external {
require(approvedPools[_poolAddress], "Pool address not approved");
uint256 poolRegistryEntry = QuantAMMWeightedPool(_poolAddress).poolRegistry();
if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
} else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
require(msg.sender == quantammAdmin, "ONLYADMIN");
} else {
revert("No permission to set weight values");
}
for (uint i; i < _weights.length; i++) {
if (i < _numberOfAssets) {
require(_weights[i] > 0, "Negative weight not allowed");
require(_weights[i] < 1e18, "Weight greater than 1 not allowed");
}
}
IQuantAMMWeightedPool(_poolAddress).setWeights(_weights, _poolAddress, _lastInterpolationTimePossible);
emit SetWeightManual(msg.sender, _poolAddress, _weights, _lastInterpolationTimePossible);
}