Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 123/168
Findings: 1
Award: $60.77
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
60.7742 USDC - $60.77
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/manager/Manager.sol#L209
Manager.sol implements a different pattern to contract upgradeability only performing an authorisation check and not ensuring that the new Manager implementation has been registered as an upgrade via isRegisteredUpgrade()
.
The impact is that an upgrade to the Manager.sol does not require a two step approval and be registered via registerUpgrade()
. Additionally there is no notification event that the Manager implementation has been registered for an upgrade i.e. UpgradeRegistered
.
In this respect the Manager.sol contract has a different implementation to other contracts that make up the DAO (e.g. Token.sol and Governor.sol) and doesnโt follow the process described in the IManager.sol interface, namely that upgrades are registered via registerUpgrade()
. and therefore emit the UpgradeRegistered
event for transparency and monitoring/auditing.
When comparing _authorizeUpgrade()
in Manager.sol and Token.sol the implementations differ;
// Manager.sol function _authorizeUpgrade(address _newImpl) internal override onlyOwner {} // Token.sol function _authorizeUpgrade(address _newImpl) internal view override { // Ensure the caller is the shared owner of the token and metadata renderer if (msg.sender != owner()) revert ONLY_OWNER(); // Ensure the implementation is valid if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl); }
When the Manager.sol implementation is updated it will not check whether a new implementation has been registered. The upgradeTo()
function in UUPS.sol will be called checking authorisation and then upgrading the implementation;
// UUPS.sol function upgradeTo(address _newImpl) external onlyProxy { _authorizeUpgrade(_newImpl); _upgradeToAndCallUUPS(_newImpl, "", false); }
However unlike Token.sol, Manager.sol performs no checks as to whether the implementation has been registered only checking that the calling entity is the owner.
Vim
To make Manager.sol consistent with the IManager interface and other contracts in the DAO it should have the same functionality implemented in _authoriseUpgrade()
(see below);
function _authorizeUpgrade(address _newImpl) internal view override onlyOwner { if (!this.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl); }
As well as this the NounsBuilderTest.sol should be updated to perform .registerUpgrade()
before .upgradeTo()
. For example;
// L71 of NounsBuilderTest.sol vm.startPrank(zoraDAO); manager.registerUpgrade(managerImpl0, address(managerImpl)); manager.upgradeTo(managerImpl); vm.stopPrank(); }
Then all tests can be run and they will pass.
#0 - GalloDaSballo
2022-09-27T01:25:44Z
I'm not convinced that the Manager needs to follow the same upgrade pattern as other logics, because Manager is by definition a Singleton (one per chain), while the other contracts are meant to be proxy to logics which are deployed for each DAOs
1-many if you will
For that reason, the admin (the Sponsor's DAO) may elect to use a different pattern, and that should not be cause for concern.
Even if we conceded that the pattern is unusual, I fail to see any vulnerability.
With the Sponsor confirming, in lack of a vulnerability, I'll downgrade to QA - Refactoring
#1 - GalloDaSballo
2022-09-27T01:25:46Z
R