QuantAMM

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

lack of adequate access controls in `UpdateWeightRunner::setRuleForPool`

Summary

The setRuleForPool function within the UpdateWeightRunner.sol contract of the QuantAMM protocol lacks adequate access controls, permitting any external entity to assign or modify update rules for arbitrary pools.

Vulnerability Details

This is the code for UpdateWeightRunner::setRuleForPool:

function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external {
require(address(rules[msg.sender]) == address(0), "Rule already set");
require(_poolSettings.oracles.length > 0, "Empty oracles array");
require(poolOracles[msg.sender].length == 0, "pool rule already set");
for (uint i; i < _poolSettings.oracles.length; ++i) {
require(_poolSettings.oracles[i].length > 0, "Empty oracles array");
for (uint j; j < _poolSettings.oracles[i].length; ++j) {
if (!approvedOracles[_poolSettings.oracles[i][j]]) {
revert("Not approved oracled used");
}
}
}
// Logic to set oracles and rules
// ...
}

Inadequate Access Control
Unrestricted Access: The setRuleForPool function is marked as external, allowing any external address to invoke it. There are no checks to verify if the caller (msg.sender) is an authorized pool contract.

Lack of Caller Verification: The function solely checks if a rule is already set for the caller and validates the provided oracles. It does not confirm whether the caller is a legitimate pool deployed via the trusted factory.

Potential for Malicious Rule Assignment: Without restricting access, malicious actors can register fake pools with arbitrary or harmful rules, disrupting the protocol's intended behavior.

Vulnerability

Lack of Authorized Caller Verification:

function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external {
// No verification if msg.sender is a legitimate pool
require(address(rules[msg.sender]) == address(0), "Rule already set");
// ...
}

Since the function is external and lacks restrictions, any EOA or contract can call setRuleForPool.

Impact

Potential Exploits Due to Vulnerability

Unauthorized pool configuration:

Malicious actors can deploy arbitrary contracts and invoke setRuleForPool, registering them as legitimate pools with custom update rules.
These fake pools can manipulate weights, skew liquidity distributions, and disrupt normal trading operations within the protocol.

Economic exploits:

By setting extreme or malicious weight update rules, attackers can create scenarios that drain liquidity, cause excessive slippage, or disrupt the balance of assets within legitimate pools.
Users may face financial losses, and the overall trust in the protocol could be eroded, leading to reduced adoption and potential insolvency of pools.

Denial of Service (DoS):

Attackers could set computationally intensive rules or flood the protocol with malicious pools, consuming excessive gas and rendering the protocol unusable.
Legitimate users and pools may experience failed transactions, increased gas costs, and halted trading activities.

Proof of Concept (PoC)

The following demonstrates how an unauthorized caller can exploit the setRuleForPool function to register a malicious pool.

Malicious Pool Contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IQuantAMMWeightedPool.sol";
contract MaliciousPool {
IUpdateWeightRunner public updateWeightRunner;
constructor(address _updateWeightRunner) {
updateWeightRunner = IUpdateWeightRunner(_updateWeightRunner);
}
function registerMaliciousRule() external {
IQuantAMMWeightedPool.PoolSettings memory settings;
settings.oracles = new address;
settings.oracles;
settings.oracles[0][0] = address(0); // Invalid oracle
// Assigning a non-approved rule contract
settings.rule = address(this);
settings.lambda = new uint64;
settings.lambda[0] = 1;
settings.epsilonMax = 1;
settings.absoluteWeightGuardRail = 1;
settings.updateInterval = 1;
settings.poolManager = msg.sender;
updateWeightRunner.setRuleForPool(settings);
}
}

Deploy MaliciousPool with the address of the UpdateWeightRunner.
Invoke registerMaliciousRule from MaliciousPool

The protocol now contains a pool with an invalid oracle configuration, potentially causing failed weight updates or other unintended behaviors.

If the malicious rule contract is designed to exploit specific functionalities, it can manipulate pool weights to the attacker's advantage.

Tools Used

Code review, Foundry, Chatpgt-o1, Claud3.5, Github copilot, slither, aderyn, Solidity Metrics

Recommendations

Implement strict access controls on setRuleForPool in UpdateWeightRunner.sol:

mapping(address => bool) public isRegisteredPool;
modifier onlyRegisteredPool() {
require(isRegisteredPool[msg.sender], "Caller is not a registered pool");
_;
}
function registerPool(address _pool) external onlyRole(FACTORY_ROLE) {
require(_pool != address(0), "Invalid pool address");
require(!isRegisteredPool[_pool], "Pool already registered");
isRegisteredPool[_pool] = true;
emit PoolRegistered(_pool);
}
function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external onlyRegisteredPool {
require(address(rules[msg.sender]) == address(0), "Rule already set");
require(_poolSettings.oracles.length > 0, "Empty oracles array");
require(poolOracles[msg.sender].length == 0, "Pool rule already set");
for (uint i; i < _poolSettings.oracles.length; ++i) {
require(_poolSettings.oracles[i].length > 0, "Empty oracles array");
for (uint j; j < _poolSettings.oracles[i].length; ++j) {
require(approvedOracles[_poolSettings.oracles[i][j]], "Not approved oracle used");
}
}
// Logic to set oracles and rules
// ...
}

Enhance parameter validation

Implement comprehensive checks to validate the integrity and compatibility of the provided PoolSettings, ensuring that rules and oracles are correctly configured.

function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external onlyRegisteredPool {
// Existing access controls...
// Additional validation
require(_poolSettings.rule != address(0), "Invalid rule contract");
require(_poolSettings.rule.supportsInterface(type(IUpdateRule).interfaceId), "Rule does not implement IUpdateRule");
// Logic to set oracles and rules
// ...
}

Find a way to guarantee that only pools created via the factory are recognized and authorized by the UpdateWeightRunner, mitigating risks of unauthorized pool registrations

Updates

Lead Judging Commences

n0kto Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
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.

Give us feedback!