QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: low
Invalid

Unauthorized Pool Weight Manipulation via Untrusted Registry in `setWeightsManually()`

Summary

wrong access control check in UpdateWeightRunner.sol:setWeightsManually() function that enables malicious pool creators to bypass intended authorization checks and manipulate pool weights without proper approval.

Vulnerability Details

The vulnerability is located in the access control logic of setWeightsManually() function in UpdateWeightRunner:

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");
}
// ...
}

The function retrieves poolRegistry directly from the pool contract, which was set by normal users during pool creation in QuantAMMWeightedPoolFactory or can be set for custom Logic Pools:

pool = _create(abi.encode(
QuantAMMWeightedPool.NewPoolParams({
poolRegistry: params.poolRegistry, // Attacker-controlled
// ...other params
})

Key issues:

  • setWeightsManually() checks access control against user-provided poolRegistry instead of admin-controlled approvedPoolActions

  • No validation that pool creator is actually approved for weight modifications

  • Violates protocol's trust model for type 3 pools where "pool creator is deemed untrusted"

Attack Path:

  1. Attacker creates pool with crafted poolRegistry value setting MASK_POOL_OWNER_UPDATES bit

  2. Calls setWeightsManually() despite lacking proper approval

  3. Successfully modifies pool weights due to incorrect access control check

  4. Can manipulate weights for economic gain

Impact

  • Bypass of intended access controls in setWeightsManually()

  • Unauthorized weight manipulation

Tools Used

  • Manual code review

Recommendations

1- Fix access control in setWeightsManually():

function setWeightsManually(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible,
uint _numberOfAssets
) external {
// Check against admin-controlled mapping instead of user-provided value
uint256 poolRegistryEntry = approvedPoolActions[_poolAddress];
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");
}
// Continue with weight updates...
}

2- Factory-level protections:

  • Implement validation during pool creation to prevent setting of privileged bits in poolRegistry

  • Move registry management to factory contract

Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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