Platform: Code4rena
Start Date: 25/08/2022
Pot Size: $75,000 USDC
Total HM: 35
Participants: 147
Period: 7 days
Judge: 0xean
Total Solo HM: 15
Id: 156
League: ETH
Rank: 13/147
Findings: 3
Award: $2,048.36
🌟 Selected for report: 1
🚀 Solo Findings: 0
1714.8718 DAI - $1,714.87
https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/policies/Governance.sol#L164 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/policies/Governance.sol#L217-L218 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/policies/Governance.sol#L268
Before any VOTES
are minted anyone can activate and execute an arbitrary proposal even with 0 votes cast. So an attacker can pass any proposal (i.e. change the executor
+ admin
of the Kernel
, gaining access to all permissioned functions and to funds held).
Checks on vote numbers made in Governance.sol
at lines L164, 217-218, 268 pass if VOTES.totalSupply() == 0
. So, until no VOTES
are minted, anyone can submit, activate and execute a proposal. There is no need to own or cast votes. This happens if OlympusGovernance
is granted the executor
role before any VOTES
are minted (as in Governance.t.sol). The attacker can anticipate/frontrun the minting and pass a proposal to change both the Kernel
admin
and executor
. Then he/she can upgrade malicious modules, steal funds from treasury...
A PoC was obtained modifying the setUp()
of Governance.t.sol by keeping only what is before the minting of VOTES
(up to L83 included). The test is as follows:
function test_AttackerPassesProposalBeforeMinting() public { address[] memory users = userCreator.create(1); address attacker = users[0]; vm.prank(attacker); MockMalicious attackerControlledContract = new MockMalicious(); Instruction[] memory instructions_ = new Instruction[](2); instructions_[0] = Instruction(Actions.ChangeAdmin, address(attackerControlledContract)); instructions_[1] = Instruction(Actions.ChangeExecutor, address(attackerControlledContract)); vm.prank(attacker); governance.submitProposal(instructions_, "proposalName", "This is the proposal URI"); governance.endorseProposal(1); vm.prank(attacker); governance.activateProposal(1); vm.warp(block.timestamp + 3 days + 1); governance.executeProposal(); assert(kernel.executor()==address(attackerControlledContract)); assert(kernel.admin()==address(attackerControlledContract)); }
with
contract MockMalicious {}
In Governance.sol
check for a minimum VOTES totalSupply, similiar to the expected initial supply of VOTES when they have been fairly distributed, for example at line L164.
#0 - 0xean
2022-09-16T23:07:10Z
Leaving as High severity as this shows a clear path to loss of funds.
#1 - Wanan098
2023-03-29T16:58:44Z
This is very serious problem. If there is only one hole in project, it will be serious.
🌟 Selected for report: rbserver
Also found by: 0x1f8b, Bahurum, csanuragjain, yixxas
250.0283 DAI - $250.03
Judge has assessed an item in Issue #496 as Medium risk. The relevant finding follows:
#0 - 0xean
2022-09-22T21:30:50Z
closing as dupe of #239
#1 - 0xean
2022-09-22T21:31:33Z
Upgrade L01 from wardens QA report.
🌟 Selected for report: zzzitron
Also found by: 0x040, 0x1f8b, 0x52, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSky, 0xSmartContract, 0xkatana, 8olidity, Aymen0909, Bahurum, BipinSah, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chandr, Chom, CodingNameKiki, Deivitto, DimSon, Diraco, ElKu, EthLedger, Funen, GalloDaSballo, Guardian, IllIllI, JansenC, Jeiwan, Lambda, LeoS, Margaret, MasterCookie, PPrieditis, PaludoX0, Picodes, PwnPatrol, RaymondFam, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StevenL, The_GUILD, TomJ, Tomo, Trust, Waze, __141345__, ajtra, ak1, apostle0x01, aviggiano, bin2chen, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch13fd357r0y3r, cloudjunky, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, devtooligan, dipp, djxploit, durianSausage, eierina, enckrish, erictee, fatherOfBlocks, gogo, grGred, hansfriese, hyh, ignacio, indijanc, itsmeSTYJ, ladboy233, lukris02, martin, medikko, mics, natzuu, ne0n, nxrblsrpr, okkothejawa, oyc_109, p_crypt0, pfapostol, prasantgupta52, rajatbeladiya, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, sorrynotsorry, tnevler, tonisives, w0Lfrum, yixxas
83.4618 DAI - $83.46
VOTES
module can lead to governance vulnerabilitiesThe comment in VOTES.sol#L10:
/// @dev This is currently a substitute module that stubs gOHM.
means that the VOTES
module is temporary and will be replaced by a form of governance token (could be staked gOHM for example). One must be sure before upgrading that the new token does not present a transfer
function nor any mechanism or bypass that effectively allows to transfer tokens in between addresses. If the contrary is true, it would lead to a double endorsing issue (possibility to call OlympusGovernance.endorseProposal()
a second time after getting the same tokens to a different address). Note that a bypass that allows transfer of tokens without delay could be used by an attacker to get unlimited endorsement power and execute a DoS on the governance by frontrunning calls to activateProposal()
with a self-endorsed proposal.
When calling vote()
on the governance, VOTES
token are transfered temporarily to the governance contract (Governance.sol#L259), so one must wait before being able to endorse a proposal again.
Change of critical system parameters should come with an event emission to allow for monitoring of potentially dangerous or malicious changes. Occurrencies of this issue are Kernel.sol#L77, Kernel.sol#L127, BondCallback.sol#L190
executor
can become also admin
At Kernel.sol#L253 the executor
is allowed to become also admin
of the Kernel
. Consider checking that the admin
set is not the executor
(and vice versa at Kernel.sol#L251) to avoid full centralization of the system.
TODO
sOpen TODOs can point to architecture or programming issues that still need to be resolved. Occurrencies at TreasuryCustodian.sol#L51-L56, Operator.sol#L657. All issues raised in TODOs should be addressed before deployment.
missing Natspec comments for public / external functions:
missing Natspec comments for public getters:
missing function parameters:
missing function return values
incorrect comments:
Mapping of keycode to module address
Mapping of keycode to module address
Policy
and Keycode
should be invertedConsider adding missing Natspec comments according to the relevant section in the solidity docs.
At Governance.sol#L223 and Governance.sol#L306 use of bool == true
is equivalent to just bool
.
netVotes
should underflowIn QA.md#L82 check that yesVotesForProposal[activeProposal.proposalId] > noVotesForProposal[activeProposal.proposalId]
before subtracting and revert with custom error NotEnoughVotesToExecute
if not. This avoids to have a Panic error due to underflow in case netVotes
should underflow.