FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: medium
Likelihood: medium

Factory Guardrails Can Be Bypassed Via Direct Cloning Leading To Unauthorized Pool Initialization

Author Revealed upon completion

Bypassing Factory Allowlist and Access Controls via Direct Clone Initialization

Description

The ConfidencePoolFactory enforces important protocol rules during pool deployment, such as verifying that the stake token is allowlisted (isTokenAllowlisted) and ensuring that only the agreement owner can create the pool.

However, ConfidencePool.initialize() does not restrict who can call it. It lacks any check to confirm that msg.sender is actually the factory contract.

Because of this, an attacker can completely bypass the factory. They can independently deploy a standard EIP-1167 minimal proxy pointing to the ConfidencePool implementation and call initialize() directly. This allows the attacker to force-initialize a pool linked to a legitimate agreement but with malicious configurations, such as non-allowlisted tokens, or setting themselves as the moderator and recovery address.


function initialize(address _agreement, address _stakeToken, address _moderator, address _recoveryAddress) external initializer {

Risk

Likelihood:

  • Anyone can interact directly with the blockchain to deploy minimal proxies (EIP-1167 clones) and trigger the unconstrained initialize function without factory authorization.

Impact:

  • This breaks the core deployment invariants. Users tracking valid agreements could be deceived into interacting with these independently cloned pools. Since the attacker controls the moderator and recovery permissions on these rogue clones, any user funds deposited into them can be swept and stolen.


Proof of Concept

The issue is verified in the following test file: test/poc/UnauthorizedCloneInitialization.t.sol
The test demonstrates that an unauthorized actor can successfully spin up a clone, pass a valid agreement address, and execute initialize() with custom malicious parameters without interacting with the factory.
The automated fuzz and unit tests execute the initialization function directly on a newly deployed minimal proxy clone. It bypasses the ConfidencePoolFactory completely, confirming that roles can be self-assigned without owner validation."

Recommended Mitigation

//Restrict the initialize function by adding an immutable factory state variable. This ensures the pool clone can only be initialized during the official deployment pipeline triggered by the factory
- function initialize(address _agreement, address _stakeToken, address _moderator, address _recoveryAddress) external initializer {
+ address public immutable factory;
+
+ constructor(address _factory) {
+ factory = _factory;
+ _disableInitializers();
+ }
+
+ function initialize(address _agreement, address _stakeToken, address _moderator, address _recoveryAddress) external initializer {
+ require(msg.sender == factory, "Unauthorized");

Support

FAQs

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

Give us feedback!