Olympus DAO contest - Lambda's results

Version 3 of Olympus protocol, a decentralized floating currency.

General Information

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

Olympus DAO

Findings Distribution

Researcher Performance

Rank: 5/147

Findings: 6

Award: $3,033.35

🌟 Selected for report: 2

🚀 Solo Findings: 1

Findings Information

🌟 Selected for report: rbserver

Also found by: GalloDaSballo, Guardian, Lambda, __141345__, cccz, csanuragjain, m9800, zzzitron

Labels

bug
duplicate
2 (Med Risk)

Awards

91.1353 DAI - $91.14

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/policies/Governance.sol#L247

Vulnerability details

Impact

When a user has already voted for the active proposal, all subsequent calls to vote fail. However, it can happen that the user receives new VOTES tokens after voting. These tokens cannot be used to vote for the current proposal and are effectively frozen until a new proposal is active. Even worse, because VOTES.totalSupply() is considered in executeProposal(), it can become much harder (or impossible) to execute a proposal.

Proof Of Concept

Let's consider an extreme example: User A has 1 VOTES token and votes for the active proposal. The totalSupply() of VOTES at that time is 10. Then, user A gets additional 30 VOTES tokens. However, those cannot be used to vote on the active proposal. For this proposal, it will be impossible to reach the execution threshold, as the maximum number of votes that it can get is 10, but the totalSupply() now is 40.

Allow increasing the votes for the active proposal.

#0 - fullyallocated

2022-09-04T02:51:06Z

Duplicate of #275

Findings Information

Awards

11.0311 DAI - $11.03

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/modules/PRICE.sol#L167 https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/modules/PRICE.sol#L173

Vulnerability details

Impact

Chainlink can return negative values for a price feed (for instance temporary for a short time), as asset prices can be negative (e.g., oil in 2020). Currently, this is not handled correctly and will completely break the system: As the value is directly casted to a uint256, this will lead to huge and therefore completely wrong values.

Note that a reserveEthPrice of 0 is also not handled explicitly, but will lead to a reversion (which is probably the best thing in such a scenario).

Proof Of Concept

Chainlink temporarily returns a RESERVE/ETH price of -1. After the casting, reserveEthPrice is type(uint256).max, meaning that currentPrice is 0 (whereas the "true value" for a reserveEthPrice that is equal to +1 (i.e., slightly higher) would be ohmEthPrice * _scaleFactor).

Revert when the returned price is negative to avoid further calculations with completely wrong values.

#0 - Oighty

2022-09-06T19:57:40Z

Duplicate. See comment on #441.

Findings Information

🌟 Selected for report: Lambda

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1905.4132 DAI - $1,905.41

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/modules/INSTR.sol#L61

Vulnerability details

Impact & Proof Of Concept

In INSTR.sol, it is correctly checked that a ChangeExecutor instruction only occurs at the last position to avoid situations where the other instructions are deemed as invalid. However, the same problem can occur for MigrateKernel. For instance, let's say we have a MigrateKernel followed by a DeactivatePolicy action. The MigrateKernel action will change the value of kernel within the policy. The DeactivatePolicy action tries to call setActiveStatus on the policy. However, this has a onlyKernel modifier and the call will therefore fail when it is done after the value of kernel was changed.

Perform the same check for MigrateKernel.

#0 - fullyallocated

2022-09-04T03:31:10Z

Thank you; good catch

Findings Information

🌟 Selected for report: Lambda

Also found by: enckrish

Labels

bug
2 (Med Risk)

Awards

857.4359 DAI - $857.44

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/Kernel.sol#L296

Vulnerability details

Impact & Proof Of Concept

To check that an already active policy is not added a second time, isActive() is called on the policy. However, policy could be a malicious contract that always returns false for isActive(). In such a scenario, it would be possible to activate the policy multiple times for the same Kernel. This would break uniqueness invariants such that _deactivatePolicy() no longer works. However, it could also be used for a DoS attack: As _reconfigurePolicies and _migrateKernel iterate over those lists that now contain duplicates, they could run out of gas if a policy is activated enough times.

Check getPolicyIndex[policy_] != 0 instead of relying on a value of an untrusted contract.

#0 - 0xLienid

2022-09-06T15:05:46Z

@ind-igo a few other submissions also mention problems with over-reliance on policy.isActive (i.e. #368). Might be worth mitigating with the suggested step here or the check on activePolicies[index] like 368 mentions.

#1 - ind-igo

2022-09-08T19:48:41Z

Dupe of #368

#2 - 0xean

2022-09-19T22:21:22Z

I think this is separate from #368 which is about a policy deactivating that isn't already active.

I am a bit skeptical at the impact statement currently, but it does seem like protocol functionality does end up in a bad state with the typical policy lifecycle here. Will award as M unless Sponsor wants to provide some additional reasoning as to a downgrade.

#3 - ind-igo

2022-10-07T16:56:01Z

While the issue is slightly different from #368, the solution is the exact same. The remediation has the new checks to prevent both of these issues.

Findings Information

🌟 Selected for report: rvierdiiev

Also found by: Jeiwan, Lambda, Trust, datapunk, devtooligan, itsmeSTYJ, zzzitron

Labels

bug
duplicate
2 (Med Risk)
sponsor acknowledged

Awards

113.9192 DAI - $113.92

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/policies/Heart.sol#L103

Vulnerability details

Impact

lastBeat is always increased by the frequency and not set to the curernt timestamp. Therefore, if it was not called for a long time, it can be called multiple times in a row which violates frequency. As the moving average is then always updated, this also leads to wrong values for the moving average (instead of a moving average over N * frequency, it can suddenly be a moving average over just a few seconds).

Proof Of Concept

lastBeat is currently at timestamp X and we assume the frequency is 100. beat() is not called for 1000 seconds. At timestamp X + 1001, it can now be called ten times in a row and the moving average is updated every time. If these ten updates happen in the same transaction, the moving average is no longer a moving average, but simply the current price at timestamp X + 1001.

Set lastBeat to the current timestamp.

#0 - Oighty

2022-09-07T21:03:16Z

See comment on #405.

#1 - 0xean

2022-09-19T13:48:07Z

closing as dupe of #79

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter