Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Invariant is missing on a function where it should be implemented
Details
The initNoWellToken function in the Upgradable Well Implementation contract allows attackers to partially initialize the contract, bypassing critical security checks and leaving it in an inconsistent state. This enables temporary freezing of protocol operations and griefing attacks, requiring manual intervention to resolve.
Vulnerability Details
Affected Code:
// WellUpgradeable.sol (Lines 50-52)
function initNoWellToken() external initializer {}
Empty Initializer:
The function uses OpenZeppelin's initializer modifier but contains no logic to configure tokens, access controls, or security mechanisms.
Once called, it marks the contract as initialized (_initialized = 1), blocking legitimate initialization via init().
Lack of Access Control:
No onlyProxy or onlyOwner modifier exists to restrict calls to authorized deployers.
Attackers can front-run deployment transactions to execute this function first.
Protocol Impact:
Core functions (e.g., swaps, liquidity provisioning) become unusable until reinitialized.
Token validation checks in init() are skipped, potentially allowing duplicate tokens in pools.
In-Scope Impacts:
Temporary Freezing of Funds (High Severity):
Users cannot interact with the Well's core functions (e.g., swapFrom(), addLiquidity()) until reinitialized.
Griefing (Medium Severity):
Attackers can disrupt protocol operations without financial gain, damaging user trust.
Missing Invariant (Medium Severity):
No check ensures complete initialization before operational use.
Funds at Risk:
All liquidity in Wells deployed via vulnerable implementations becomes temporarily frozen.
forge test --match-test test_initNoWellTokenBypass -vvv
Expected Output
BIC Response
The issue you describe will only arise if a contract deployer uses an incorrect/malicious configuration. Since Wells are immutable, all existing Wells are unaffected by this - there is no exploit that can be executed by an attacker. Therefore we are closing this report and no reward will be issued.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/WellUpgradeable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract ExploitTest is Test {
WellUpgradeable impl;
ERC1967Proxy proxy;
WellUpgradeable well;
function setUp() public {
impl = new WellUpgradeable();
proxy = new ERC1967Proxy(address(impl), "");
well = WellUpgradeable(address(proxy));
}
function test_initNoWellTokenBypass() public {
// Attacker calls initNoWellToken first
well.initNoWellToken();
// Verify initialization state
assertEq(well.getInitializerVersion(), 1, "Partial initialization failed");
// Legitimate init() call reverts
vm.expectRevert("Initializable: contract is already initialized");
well.init("Test", "TST");
}
}