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

Inconsistent/Fake Safe Harbor Registry can be supplied during Clone Initialization

Author Revealed upon completion

Root + Impact

The initialize function of the cloned ConfidencePool does not verify whether the provided agreement_ belongs to or is consistent with the specified safeHarborRegistry_. An attacker can initialize a pool with a malicious, custom-deployed registry that returns arbitrary state to hijack pool outcomes.

Description

The factory contract deploys non-upgradeable clones of ConfidencePool and calls their initialize function. This function takes agreement_ and safeHarborRegistry_ as separate arguments.

The issue is that while initialize checks whether the agreement is registered as valid under the registry via isAgreementValid(agreement_), it does not enforce any strict architectural binding between them. An attacker can deploy a fake registry contract that returns true for isAgreementValid but provides an attacker-controlled mock AttackRegistry. Because the pool reads live state via safeHarborRegistry.getAttackRegistry(), initializing a clone with an inconsistent/mock registry allows the attacker to completely control state transitions (such as fabricating a false CORRUPTED state to sweep honest stakers' principal).

function initialize(
address agreement_,
address stakeToken_,
address safeHarborRegistry_,
// ...
) external initializer {
// ...
@> if (!IBattleChainSafeHarborRegistry(safeHarborRegistry_).isAgreementValid(agreement_)) {
@> revert InvalidAgreement();
@> }
}

Risk

Likelihood:

  • Occurs when a pool is created, either via direct interaction with the implementation proxy or if the factory owner incorrectly configures the registry parameters.

  • Allows deployment of deceptive "honeypot" pools that mirror legitimate agreements but are backed by a malicious state machine.

Impact:

  • Direct loss of principal for honest depositors who stake in the mismatched pool.

  • Arbitrary outcome manipulation (unauthorized sweeps of both principal and bonus).

Proof of Concept

function test_PoC_InitializeRegistryInconsistency() public {
FakeRegistry fakeRegistry = new FakeRegistry();
ConfidencePool p = ConfidencePool(Clones.clone(address(new ConfidencePool())));
p.initialize(agreement, address(token), address(fakeRegistry), moderator, block.timestamp + 31 days, ONE, recovery, alice, _defaultScope());
assertEq(address(p.safeHarborRegistry()), address(fakeRegistry));
}

Recommended Mitigation

Validate the registry relationship or retrieve the canonical registry directly from a trusted factory or global contract mapping rather than allowing arbitrary addresses to be set during clone initialization.

Support

FAQs

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

Give us feedback!