Proxy and upgradeability vulnerabilities describe any situation where a smart contract uses an upgradeable architecture (proxy, beacon, or implementation-swapping pattern) and the upgrade path, initialization, or admin controls are misdesigned or misconfigured. Upgradeable contracts separate a proxy (which holds state and delegates calls) from an implementation (which contains logic). When upgradeability is improperly secured, attackers can hijack the proxy admin or upgrade role to deploy malicious implementations, re-initialize contracts to seize ownership, or bypass critical checks in initialization or migration steps.
This affects all contract types that use upgradeability: DeFi (lending, vaults, DEXes), NFTs (collections, marketplaces), DAOs (governance, treasuries), bridges (messengers, asset contracts), and L2/cross-chain systems. Common patterns include Transparent Proxy, UUPS (EIP-1822), Beacon Proxy, and custom router-implementation designs. On non-EVM chains, analogous upgrade mechanisms exist (e.g., Move modules, Solana program upgrades) with similar trust and initialization risks.
Few areas to focus on:
initialize, missing initializer guards, storage collisions)delegatecall context, msg.sender/msg.value propagation)Attackers exploit:
These issues often overlap with access control (SC01) but warrant separate attention due to the systemic impact of proxy and upgrade mechanisms.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VulnerableProxyAdmin {
address public admin;
address public implementation;
constructor(address _implementation) {
// Critical: no way to set custom admin; implicitly trusts deployer logic
admin = msg.sender;
implementation = _implementation;
}
function upgrade(address newImplementation) external {
// Missing: access control (only admin) and sanity checks
implementation = newImplementation;
}
}
Issues:
upgrade; any caller can change implementation.newImplementation (e.g., interface compatibility, non-zero address).// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
contract SafeProxyAdmin is Ownable {
address public implementation;
event Upgraded(address indexed newImplementation);
error InvalidImplementation();
constructor(address _implementation) {
_setImplementation(_implementation);
_transferOwnership(msg.sender);
}
function _setImplementation(address _implementation) internal {
if (_implementation == address(0)) revert InvalidImplementation();
implementation = _implementation;
}
function upgrade(address newImplementation) external onlyOwner {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
}
Security Improvements:
upgrade is restricted to the contract owner (which should itself be a robust governance or multisig).Initialization functions (e.g., initialize(), initializer modifiers in OpenZeppelin) are critical in upgradeable patterns. Common pitfalls:
Basic example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VulnerableLogic {
address public owner;
// Missing initializer guard
function initialize(address _owner) external {
owner = _owner;
}
}
If used behind a proxy without proper initialization control, attackers can call initialize via the proxy and set themselves as owner, taking over the protocol.
Kinto Protocol (July 2025, $1.55M loss)
Attackers exploited uninitialized ERC1967 proxy contracts. They detected freshly deployed proxy contracts that had not been properly initialized, then initialized them with malicious implementations containing dormant backdoors. Months later, the attacker activated the backdoor, upgraded the proxy to malicious code, and minted K tokens directly to drain $1.55M. The vulnerability: unprotected initialization allowing anyone to become the proxy admin.
Uninitialized Proxy Campaign (2025, $10M+ across protocols)
A broader campaign targeted uninitialized ERC1967 proxies across multiple EVM chains. Attackers used automated scanning to detect newly deployed proxies before legitimate developers could initialize them, then initialized with malicious implementations. The backdoors lay dormant for months, evading audits. When activated, attackers could upgrade proxies and drain funds.
initializer and reinitializer modifiers correctly.