https://github.com/kinetic-market/public-money-market-contracts
The setImplementation
function in the CErc20Delegator
contract does not verify whether the new implementation address is a valid contract. As a result, an admin could accidentally or deliberately set an Externally Owned Account (EOA) or zero address as the new implementation, causing subsequent delegate calls to fail and effectively bricking the proxy. While a malicious admin could still deploy harmful code, adding a code-size check is a best practice to prevent accidental misconfiguration and maintain the contract’s operational reliability.
setImplementation
accepts any address for the new implementation without validating whether the address contains contract code. If the new address is non-contract (e.g., an EOA, a zero address, or an address without code), future delegatecall operations will fail, rendering the proxy inoperative. This risk stems from an absence of a basic defensive check in the proxy contract. However, it primarily affects operational reliability rather than creating a direct user-exploitable vulnerability, because the admin already retains full power to upgrade to any implementation.
Impact: Setting the implementation to a non-contract address breaks delegate calls, halting protocol functionality and potentially locking funds.
Likelihood: Low in well-governed systems, because the admin role is typically secured (via a multi-sig or timelock). Mistakes or malicious actions by the admin remain the main vector, reflecting an operational/governance risk rather than a direct exploit by external parties.
A malicious actor or an accidental mistake that sets the implementation address to an EOA causes all delegate calls to fail. For example, if the real implementation returns valid snapshots and pricing data but an EOA is accidentally set, every external call (such as getPrice
or getAccountSnapshot
) will revert, resulting in total functional disruption.
// Example test demonstrating misconfiguration leading to proxy failure
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
interface ICErc20Delegator {
function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) external;
}
contract TestEOA {
// No code, simulates an externally owned account
}
contract ImplementationAddressValidationTest is Test {
ICErc20Delegator delegator;
TestEOA eoa;
function setUp() public {
// Assume the delegator is deployed and admin is controlled by the test environment.
// The following addresses simulate the environment.
delegator = ICErc20Delegator(vm.addr(1));
eoa = new TestEOA(); // Simulate an EOA-like contract with no operational code
// As the admin, set the new implementation to the EOA address.
vm.prank(vm.addr(1));
// This call should never be allowed if a code size check is implemented.
delegator._setImplementation(address(eoa), false, "");
}
function test_nonContractImplementationBreaksProxy() public {
// This should revert if the delegate call has been broken by a non-contract implementation.
vm.expectRevert();
delegator.getAccountSnapshot(address(0xDEADBEEF));
}
}
Implement a code-size check in the setImplementation function to ensure that the provided address contains contract code.
function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public {
require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");
uint256 codeSize;
assembly { codeSize := extcodesize(implementation_) }
require(codeSize > 0, "New implementation must be a contract");
if (allowResign) {
delegateToImplementation(abi.encodeWithSignature("_resignImplementation()"));
}
address oldImplementation = implementation;
implementation = implementation_;
delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData));
emit NewImplementation(oldImplementation, implementation);
}
POC listed in summary.