Closed c4-bot-10 closed 2 months ago
saxenism (sponsor) disputed
Invalid since it is an implementation contract to be called by a proxy.
The Warden specifies that a critical function of the GenesisUpgrade
contract does not impose access control. The contract is expected to be utilized ephemerally as a temporary implementation/facet and as such, access control is not mandatory rendering this exhibit invalid.
alex-ppg marked the issue as unsatisfactory: Invalid
Hi @alex-ppg Thanks for Judging
I believe this is a valid High severity vulnerability. The attack is quite simple related to missing access control on critical function.
upgrade
function using his favorable data in param _proposedUpgrade
on Proxy contract address.fallback
function will be triggered since upgrade function is not present in proxy and here Diamond proxy pattern used so based on called function's selector the call will be re-directed to the GenesisUpgrade
implementation contract using delegatecall
.protocolVersion
and verifier
in s
storage struct var.Use those changed values in favour of his benefit Or disrupt the protocol functionality by changing those values randomly.
It's likelihood is high and impact is also high. Anyone can call upgrade
any time and change those values.
Sponsor states that this is called through proxy but then proxy need to have access control but in this case proxy also doesn't have any access control on caller.(I have added the code of proxy below for extra context)
Generally in upgradeable contracts proxy doesn't have access control the access control is implemented by implementation contract on critical functions defined inside that contract which can be called by specific role or admin. That's why I didn't emphasize about proxy code and didn't include proxy code in report but stated in finding that it can be exploited also through proxy contract. Since generally it's not the proxy's job to have access control. But implementation contract's critical functions should have access control. Since caller of proxy is treated as caller of implementation contract's function also if called through delegatecall
from proxy.
But sponsor said it is called thorough proxy that's why invalid. Then proxy need to have access control defined if implementation doesn't have acess control. Proxy doesn't protect code by default to be called from random caller. The access control need to be implemented but proxy also doesn't have any access control. Makes this finding clearly valid. You stated ephemerally but it can be called again by anyone to change those values. Proxy fallback function which also have not any access control on caller. Implementation already doesn't have any access control.
File : state-transition/chain-deps/DiamondProxy.sol
/// @dev 1. Find the facet for the function that is called.
/// @dev 2. Delegate the execution to the found facet via `delegatecall`.
fallback() external payable {
Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
// Check whether the data contains a "full" selector or it is empty.
// Required because Diamond proxy finds a facet by function signature,
// which is not defined for data length in range [1, 3].
require(msg.data.length >= 4 || msg.data.length == 0, "Ut");
// Get facet from function selector
Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig];
address facetAddress = facet.facetAddress;
//@audit No access control to prevent any random caller
@> require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
@> require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
assembly {
// The pointer to the free memory slot
let ptr := mload(0x40)
// Copy function signature and arguments from calldata at zero position into memory at pointer position
calldatacopy(ptr, 0, calldatasize())
// Delegatecall method of the implementation contract returns 0 on error
let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
// Get the size of the last return data
let size := returndatasize()
// Copy the size length of bytes from return data at zero position to pointer position
returndatacopy(ptr, 0, size)
// Depending on the result value
switch result
case 0 {
// End execution and revert state changes
revert(ptr, size)
}
default {
// Return data with length of size at pointers position
return(ptr, size)
}
}
}
Implementation code is in the finding report itself
So access control is neither in proxy fallback nor on implementation GenesisUpgrade::upgrade
function. So this function can be called by anyone through proxy.
So this vulnerability should be considered valid high.
Thanks
Hey @thenua3bhai, thanks for contributing to the PJQA process! This finding is invalid, as it is a common pattern for proxy implementations that are transiently utilized to not impose any access control. Effectively, the Diamond instance will perform a delegatecall
to the GenesisUpgrade
style contracts to perform the upgrade and will never reference it as a facet
permanently as it is an upgrade, i.e. an operation meant to be executed one time. I appreciate your effort in attempting to contextualize the exhibit further but the ruling will remain as-is and no further feedback is expected.
Lines of code
https://github.com/code-423n4/2024-03-zksync/blob/main/code/contracts/ethereum/contracts/upgrades/GenesisUpgrade.sol#L14-L17
Vulnerability details
Vulnerability Details
Since
GenesisUpgrade
inheritsBaseZkSyncUpgradeGenesis
andupgrade
function is overridden from this. The comment in BaseZkSyncUpgradeGenesis::upgrade function says This is a virtual function and should be overridden by custom upgrade implementations. But when overriding no access control were introduced resulting in calling by everyone. Since parant function upgrade also doesn't have any access control when called by super. from child. Since this function changes important params ins
storage struct variable likes.protocolVersion
,s.verifier
and many others. Due to no access control these can be changed by anyone and can be exploited by it.Vulnerable Code
GenesisUpgrade::upgrade function
BaseZkSyncUpgradeGenesis::upgrade function
Impact
Due to no access control Anyone can update important params in
s
storage struct variable likes.protocolVersion
,s.verifier
and many others. And functionality can be damaged.Tools Used
Manual Review
Recommended Mitigation
Add some type of access control on
GenesisUpgrade::upgrade
function so only authorize person can call it. like adding onlyOwnerAssessed type
Access Control