Vader Protocol contest - 0xRajeev's results

Capital efficient liquidity protocol

General Information

Platform: Code4rena

Start Date: 22/04/2021

Pot Size: $120,000 USDC

Total HM: 41

Participants: 10

Period: 7 days

Judge: LSDan

Total Solo HM: 28

Id: 5

League: ETH

Vader Protocol

Findings Distribution

Researcher Performance

Rank: 2/10

Findings: 42

Award: $40,849.60

🌟 Selected for report: 35

🚀 Solo Findings: 22

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
3 (High Risk)
sponsor disputed

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

ERC20 implementations are not always consistent. Some implementations of transfer and transferFrom could return ‘false’ on failure instead of reverting. It is safer to wrap such calls into require() statements to handle these failures.

The transfer call on L211 of transferOut() could be made on a user-supplied untrusted token address (from the different call sites) whose implementation can be malicious.

For reference, see similar finding from Consensys Diligence Audit of Aave Protocol V2

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L211

Tools Used

Manual Analysis

Use require to check the return value and revert on 0/false or use OpenZeppelin’s SafeERC20 wrapper functions.

#0 - strictly-scarce

2021-05-01T10:06:51Z

Not valid.

Since the funds came in, and did not revert, they can leave. If the call passes, then the transferout is valid.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
3 (High Risk)
sponsor confirmed

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

One of the stated protocol (review) goals is to detect susceptibility to “Any attack vectors using flash loans on Anchor price, synths or lending.” As such, USDV contract aims to protect against flash attacks using flashProof() modifier which uses the following check in isMature() to determine if currently executing contract context is at least blockDelay duration ahead of the previous context: lastBlock[tx.origin] + blockDelay <= block.number

However, blockDelay state variable is not initialized which means it has a default uint value of 0. So unless it is set to >= 1 by setParams() which can be called only by the DAO (which currently does not have the capability to call setParams() function), blockDelay will be 0 which allows current executing context (block.number) to be the same as the previous one (lastBlock[tx.origin]). This effectively allows multiple calls on this contract to be executed in the same transaction of a block which enables flash attacks as opposed to what is expected as commented on L41: "// Stops an EOA doing a flash attack in same block"

Even if the DAO can call setParams() to change blockDelay to >= 1, there is a big window of opportunity for flash attacks until the DAO votes, finalises and approves such a proposal. Moreover, such proposals can be cancelled by a DAO minority or replaced by a malicious DAO minority to launch flash attacks.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L22

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L140-L142

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L35-L44

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L174

Tools Used

Manual Analysis

Initialize blockDelay to >= 1 at declaration or in constructor.

#0 - strictly-scarce

2021-05-01T10:15:46Z

The actual issue is simply:

blockDelay state variable is not initialized

It is intended to be initialised to 1, so this is a bug. Severity: 2

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
duplicate
3 (High Risk)

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

changeDAO() is authorized to be called only from the DAO (per modifier) but DAO contract has no corresponding functionality to call changeDAO() function. As a result, DAO address cannot be changed.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L192-L196

Tools Used

Manual Analysis

Add functionality to DAO to be able to call changeDAO() of Vader.sol.

#1 - dmvt

2021-05-25T13:00:53Z

Unlike #140, #157, #158, & #159 without this functionality missing functionality in the DAO becomes a very serious issue. As a result, this one is very high risk were it to be overlooked.

Findings Information

🌟 Selected for report: cmichel

Also found by: 0xRajeev, s1m0

Labels

bug
duplicate
3 (High Risk)
addressed

Awards

8.7993 VETH - $457.56

0.2112 ETH - $527.96

External Links

Handle

0xRajeev

Vulnerability details

Impact

Anyone can cancel any finalising stage proposal by simply using newProposalID equal to an existing (oldProposalID) finalising proposal of their choice. All checks (finalising, hasMinority, isEqual) will pass for the call cancelProposal(oldProposalID, oldProposalID) because the oldProposalID has minority support (L104 check) as it’s already finalising and L105 check will also pass.

There is no need to create and vote on a new proposal with newProposalID that has minority support and has the same type string as the oldProposalID, to allow the canceling of the proposal with oldProposalID.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/DAO.sol#L102-L108

Tools Used

Manual Analysis

Perform input validation on parameters to check that newProposalID != oldProposalID in cancelProposal().

#2 - dmvt

2021-05-26T22:15:28Z

duplicate of #227

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
3 (High Risk)
sponsor disputed

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

Flash loans can significantly increase a single voter's weight and be used to impact the voting outcome. A voter can borrow a significant quantity of tokens to increase their voting weight in a transaction within which they also deterministically influence the voting outcome to their choice.

This has already happened in the case of MakerDAO governance where a flash loan was used to affect voting outcome (see https://forum.makerdao.com/t/urgent-flash-loans-and-securing-the-maker-protocol/4901) and noted by Maker team as: “a practical example for the community that flash loans can and may impact system governance”

Given that flash loans are a noted concern, the impact of it to DAO governance which can control all critical protocol parameters should be mitigated as in other places.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/DAO.sol#L158-L163

Tools Used

Manual Analysis

Account for flash loans in countMemberVotes() by using weight from previous blocks or consider capping the weight of individual voters.

#0 - strictly-scarce

2021-05-01T10:30:29Z

Not valid.

All pools use slip-based fees so flash loan attack by buying up USDV or synths is not going to work.

#1 - dmvt

2021-05-25T13:53:48Z

The funds to execute this attack do not need to come from a pool. It could be done as simply as malicious members pooling their funds in a flash loan contract, and each borrowing the funds in turn to vote.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
3 (High Risk)
sponsor confirmed

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

The internal _transfer() function is called from external facing transfer(), transferFrom() and transferTo() functions all of which have different sender addresses. It is msg.sender for transfer(), sender parameter for transferFrom() and tx.origin for transferTo(). These different senders are reflected in the sender parameter of _transfer() function. While this sender parameter is correctly used for transfer of tokens within _transfer, the call to _burn() on L129 incorrectly uses msg.sender as the burn address which is correct only in the case of the transfer() caller's context. This is incorrect for transferFrom() and transferTo() caller contexts.

This will incorrectly burn the fees from a different (intermediate contract) account for all users of the protocol interacting with the transferTo() and transferFrom() functions and lead to incorrect accounting of token balances or exceptional conditions. Protocol will break and lead to fund loss.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L129

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L122-L134

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L91-L94

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L108-L112

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L116-L119

Tools Used

Manual Analysis

Change L129 to: _burn(sender, _fee);

#0 - strictly-scarce

2021-05-01T10:34:25Z

Valid, disagree with severity though. Funds-not-at-risk.

Recommend: 2

Findings Information

🌟 Selected for report: cmichel

Also found by: 0xRajeev, s1m0

Labels

bug
duplicate
3 (High Risk)

Awards

8.7993 VETH - $457.56

0.2112 ETH - $527.96

External Links

Handle

0xRajeev

Vulnerability details

Impact

As per code comments, the calcLiquidityUnits() function is supposed to calculate:

// units = ((P (t B + T b))/(2 T B)) * slipAdjustment // P * (part1 + part2) / (part3) * slipAdjustment

While part1, part2 and part3 are calculated correctly, they are combined as: uint _units = (((P * part1) + part2) / part3); which is incorrect and should be: uint _units = ((P * (part1 + part2)) / part3);

This incorrect calculation affects the liquidity calculations in addLiquidity() of Pools.sol and will lead to incorrect accounting. Protocol will break and funds will be lost.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Utils.sol#L229-L242

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L69

Tools Used

Manual Analysis

Fix the formulae used in the calculation as per code comments by changing L239 to: uint _units = ((P * (part1 + part2)) / part3);

#1 - dmvt

2021-05-26T22:19:34Z

duplicate of #204

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
3 (High Risk)
sponsor disputed

Awards

32.5901 VETH - $1,694.68

0.7822 ETH - $1,955.40

External Links

Handle

0xRajeev

Vulnerability details

Impact

Incorrect initialization of timeForFullProtection to1 sec instead of 8640000 secs (100 days) as indicated in code comments appears to be a test setting mistakenly carried over for deployment. Therefore, unless timeForFullProtection is reset to 100 days by setParams() (calling this function is a missing functionality in the DAO currently), the Impermanent Loss (IL) protection "rule" of 100 days will not apply in Utils.getProtection().

This breaks a key value proposition of the Vader protocol which is IL protection as indicated in the specification: “Impermanent Loss Protection: The deposit value for each member is recorded when they deposit. When they go to withdraw, the redemption value is computed. If it is less than the deposit value, the member is paid the deficit from the reserve. The protection issued increases from 0 to 100% linearly for 100 days.”

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L84

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L95

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L209-L220

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Utils.sol#L128-L140

Tools Used

Manual Analysis

Change to “timeForFullProtection = 8640000; //100 days” on L84.

#0 - strictly-scarce

2021-05-01T09:44:28Z

It's deliberately set to 1 second to conduct adequate testing.

Findings Information

🌟 Selected for report: cmichel

Also found by: 0xRajeev

Labels

bug
duplicate
disagree with severity
3 (High Risk)
sponsor disputed

Awards

14.6655 VETH - $762.61

0.352 ETH - $879.93

External Links

Handle

0xRajeev

Vulnerability details

Impact

Given the lack of input validation to check if token parameter is already listed, a malicious user can list same token multiple times to reach the anchorLimit (default=5) and prevent others from listing anchors. This will impact all anchor related accounting and logic in the protocol. Protocol will break and funds may be lost.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L245-L252

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L86

Tools Used

Manual Analysis

Check and reject if token already exists in arrayAnchors as part of input validation in listAnchor() function.

#0 - strictly-scarce

2021-05-01T09:45:49Z

Deployer will list the 5 starting stablecoins themselves, this is in the whitepaper.

#1 - dmvt

2021-05-26T22:21:46Z

duplicate of #211

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: pauliax

Labels

bug
disagree with severity
3 (High Risk)
sponsor confirmed

Awards

14.6655 VETH - $762.61

0.352 ETH - $879.93

External Links

Handle

0xRajeev

Vulnerability details

Impact

The flipMinting() function can disable/stop conversion/redeeming of VADER<>USDV tokens upon DAO approval (when that functionality is added). When minting is disabled (i.e. false), the convert functions in USDV.sol accept VADER tokens from sender (L170) but do not burn them to mint the sender the equivalent USDV tokens. When minting is disabled (i.e. false), the redeem functions in USDV.sol accept USDV tokens from sender (L188) but do not burn them to mint the sender the equivalent VADER tokens. Both paths silently return 0 without reverting the transaction thus trapping the sent tokens and leaving the users with lost funds. Protocol will break and funds will be lost.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L171-L177

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L174-L181

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L165-L172

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/USDV.sol#L183-L191

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L238-L243

Tools Used

Manual Analysis

Revert in the paths (instead of silently returning) when minting is disabled so that tokens are not accepted for conversion or redemption.

#0 - strictly-scarce

2021-05-01T07:55:44Z

Valid, funds would be lost if minting disabled and users won't have any recourse.

#1 - strictly-scarce

2021-05-01T13:21:13Z

Recommend Severity 2 as this is minor funds loss in an edge case (compare with an exploit)

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: jvaqa

Labels

bug
disagree with severity
3 (High Risk)
sponsor acknowledged

Awards

14.6655 VETH - $762.61

0.352 ETH - $879.93

External Links

Handle

0xRajeev

Vulnerability details

Impact

Incorrect initialization (perhaps testing parameterization mistakenly carried over to deployment) of secondsPerEra to 1 sec instead of 86400 secs (1 day) causes what should be the daily emission rate to be a secondly emission rate.

This causes inflation of VADER token and likely breaks VADER<>USDV peg and other protocol invariants. Protocol will break and funds will be lost.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L67

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L68

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L204-L214

Tools Used

Manual Analysis

Initialize secondsPerEra to 86400 on L67.

#0 - strictly-scarce

2021-05-01T10:17:00Z

This is purely for testing purposes.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
2 (Med Risk)
sponsor acknowledged

Awards

9.777 VETH - $508.40

0.2346 ETH - $586.62

External Links

Handle

0xRajeev

Vulnerability details

Impact

Functions removeLiquidity() and removeLiquidityDirectly() when called directly, do not provide the the user with IL protection unlike when calling the corresponding removeLiquidity() function in Router.sol. This should be prevented, at least for removeLiquidity() or highlighted in the specification and user documentation.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L77-L82

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L113-L118

Tools Used

Manual Analysis

Add access control (e.g. via a modifier onlyRouter) so removeLiquidity() function of Pools contract can be called only from corresponding Router contract’s removeLiquidity() function which provides IL protection. Alternatively, highlight in the specification and user documentation about which contract interfaces provide IL protection to users.

#0 - strictly-scarce

2021-05-01T11:04:21Z

User should use the Router, as designed.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
2 (Med Risk)
sponsor acknowledged

Awards

9.777 VETH - $508.40

0.2346 ETH - $586.62

External Links

Handle

0xRajeev

Vulnerability details

Impact

Given that there are only three proposal types (GRANT, UTILS, REWARD) that are actionable, it is unclear if 'DAO' type checked in voteProposal() is a typographical error and should really be 'GRANT'. Otherwise, GRANT proposals will only require quorum (33%) and not majority (50%).

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/DAO.sol#L79-L92

Tools Used

Manual Analysis

Change ‘DAO’ on L83 to ‘GRANT’ or if not, specify what DAO proposals are and how GRANT proposals should be handled with quorum or majority.

Also, check and enforce that mapPID_types are only these three actionable proposal types: GRANT, UTILS, REWARD.

#0 - strictly-scarce

2021-05-01T11:14:40Z

DAO not yet fully implemented

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

9.777 VETH - $508.40

0.2346 ETH - $586.62

External Links

Handle

0xRajeev

Vulnerability details

Impact

There is no input validation in replacePool() function to check if oldToken exists and is curated. Using a non-existing oldToken (even 0 address) passes the check on L236 (because Pools.getBaseAmount() will return 0 for the non-existing token) and newToken will be made curated. This can be used to bypass the curatedPoolLimit enforced only in curatePool() function.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L234-L241

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L227-L229

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Router.sol#L227

Tools Used

Manual Analysis

Check if oldToken exists and is curated as part of input validation in replacePool() function.

#0 - strictly-scarce

2021-05-01T11:03:42Z

Valid

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: gpersoon, pauliax

Labels

bug
disagree with severity
2 (Med Risk)
addressed

Awards

2.6398 VETH - $137.27

0.0634 ETH - $158.39

External Links

Handle

0xRajeev

Vulnerability details

Impact

The deploySynth() function in Pools.sol is expected to perform a check on the token parameter to determine that it is neither VADER or USDV before calling Factory’s deploySynth() function.

However, the require() incorrectly uses ‘||’ operator instead of ‘&&’ which allows both VADER and USDV to be supplied as the token parameters. This will allow an attacker to deploy either VADER or USDV as a Synth which will break assumptions throughout the entire protocol. Protocol will break and funds may be lost.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L138

Tools Used

Manual Analysis

Change ‘||’ operator to ‘&&’ in the require statement: require(token != VADER && token != USDV);

#0 - strictly-scarce

2021-05-01T10:01:28Z

Findings Information

🌟 Selected for report: gpersoon

Also found by: 0xRajeev, cmichel, jvaqa

Labels

bug
duplicate
disagree with severity
2 (Med Risk)

Awards

1.7819 VETH - $92.66

0.0428 ETH - $106.91

External Links

Handle

0xRajeev

Vulnerability details

Impact

Given the public access, this is susceptible to front-running by an attacker who can initialize this with arbitrary POOLS address before the deployer. Reinitialization will require contract redeployment because initialization can be done only once.

Reference: See finding #12 from Trail of Bits audit of Hermez Network: https://github.com/trailofbits/publications/blob/master/reviews/hermez.pdf

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Factory.sol#L27-L31

Tools Used

Manual Analysis

Use a factory pattern that will deploy and initialize atomically to prevent front-running of the initialization, or

Ensure the deployment scripts are robust in case of a front-running attack or

Given that contracts are not using delegatecall proxy pattern, it is not required to use a separate init() function to initialize parameters when the same can be done in a constructor. If the reason for doing so is to get the deployment addresses of the various contracts which may not all be available at the same time then consider rearchitecting to create a “globals” contract which can hold all the globally required addresses of various contracts. (see Maple protocol’s https://github.com/maple-labs/maple-core/blob/develop/contracts/MapleGlobals.sol for example)

#1 - dmvt

2021-05-26T22:50:45Z

duplicate of #18

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
2 (Med Risk)
sponsor disputed

Awards

9.777 VETH - $508.40

0.2346 ETH - $586.62

External Links

Handle

0xRajeev

Vulnerability details

Impact

All the external/public functions of Pools.sol can be called by other contracts even before Pools.sol contract is initialized. This can lead to exceptions, state corruption or incorrect accounting in other contracts, which may require redeployment of contract.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L43-L50

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L54

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L77

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L101

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L179

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L184

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L215

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Pools.sol#L224

Tools Used

Manual Analysis

Use a factory pattern that will deploy and initialize atomically to prevent front-running of the initialization, or

Given that contracts are not using delegatecall proxy pattern, it is not required to use a separate init() function to initialize parameters when the same can be done in a constructor. If the reason for doing so is to get the deployment addresses of the various contracts which may not all be available at the same time then consider rearchitecting to create a “globals” contract which can hold all the globally required addresses of various contracts. (see Maple protocol’s https://github.com/maple-labs/maple-core/blob/develop/contracts/MapleGlobals.sol for example) or

Prevent external/public functions from being called until after initialization is done by checking initialization state tracked by the inited variable.

#1 - dmvt

2021-05-25T10:58:05Z

Same general comments apply to this issue as with issue #18, but it is a separate type of exploit that would be slightly less detectable. This increase in risk is balanced against the exploit being much harder to effect and the likely impact being lower.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
disagree with severity
2 (Med Risk)
sponsor confirmed

Awards

9.777 VETH - $508.40

0.2346 ETH - $586.62

External Links

Handle

0xRajeev

Vulnerability details

Impact

changeDAO() updates DAO address in one-step. If an incorrect address is mistakenly used (and voted upon) then future administrative access or recovering from this mistake is prevented because onlyDAO modifier is used for changeDAO(), which requires msg.sender to be the incorrectly used DAO address (for which private keys may not be available to sign transactions).

Reference: See finding #6 from Trail of Bits audit of Hermez Network: https://github.com/trailofbits/publications/blob/master/reviews/hermez.pdf

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vader.sol#L192-L196

Tools Used

Manual Analysis

Use a two-step process where the old DAO address first proposes new ownership in one transaction and a second transaction from the newly proposed DAO address accepts ownership. A mistake in the first step can be recovered by granting with a new correct address again before the new DAO address accepts ownership. Ideally, there should also be a timelock enforced before the new DAO takes effect.

#0 - strictly-scarce

2021-05-01T10:21:20Z

A lot has to go wrong to get to this point, so disagree with severity (funds not at risk).

Two step-process seems wise though.

#1 - dmvt

2021-05-25T13:02:37Z

Risk lowered because of the extremely low probability

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: pauliax

Labels

bug
disagree with severity
2 (Med Risk)
sponsor confirmed

Awards

4.3997 VETH - $228.78

0.1056 ETH - $263.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

The conditional in calcReward() function uses the same code in both if/else parts with repeated use of reserveUSDV, reserveVADER and getUSDVAmount leading to incorrect computed value of _adjustedReserve in the else part.

This will affect harvest rewards for all users of the protocol and lead to incorrect accounting. Protocol will break and lead to fund loss.

Proof of Concept

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vault.sol#L141

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vault.sol#L144

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vault.sol#L125

https://github.com/code-423n4/2021-04-vader/blob/3041f20c920821b89d01f652867d5207d18c8703/vader-protocol/contracts/Vault.sol#L105

Tools Used

Manual Analysis

Change variables and function calls from using USDV to VADER in the else part of the conditional which has to return the adjusted reserves when synth is not an asset i.e. an anchor and therefore base is VADER.

L144 should be changed to: uint _adjustedReserve = iROUTER(ROUTER).getVADERAmount(reserveUSDV()) + reserveVADER();

#0 - strictly-scarce

2021-05-01T08:05:22Z

Funds are not at-risk, just that some users will get less rewards, some will get more.

Recommend: 2

#1 - Mervyn853

2021-05-01T08:49:12Z

Our decision matrix for severity:

0: No-risk: Code style, clarity, off-chain monitoring (events etc), exclude gas-optimisations 1: Low Risk: UX, state handling, function incorrect as to spec 2: Funds-Not-At-Risk, but can impact the functioning of the protocol, or leak value with a hypothetical attack path with stated assumptions, but external requirements 3: Funds can be stolen/lost directly, or indirectly if a valid attack path shown that does not have handwavey hypotheticals.

Recommended: 2

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