Mochi contest - defsec's results

Next-Gen Decentralized Digital Currency Backed By Long-Tail Cryptoassets.

General Information

Platform: Code4rena

Start Date: 21/10/2021

Pot Size: $80,000 ETH

Total HM: 28

Participants: 15

Period: 7 days

Judge: ghoulsol

Total Solo HM: 19

Id: 42

League: ETH

Mochi

Findings Distribution

Researcher Performance

Rank: 10/15

Findings: 4

Award: $1,094.87

🌟 Selected for report: 3

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: loop

Also found by: WatchPug, cmichel, defsec, gzeon, leastwood, nikitastupin, pants

Labels

bug
duplicate
2 (Med Risk)

Awards

0.0174 ETH - $72.55

External Links

Handle

defsec

Vulnerability details

Impact

It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.

Reference: This similar medium-severity finding from Consensys Diligence Audit of Fei Protocol: https://consensys.net/diligence/audits/2021/01/fei-protocol/#unchecked-return-value-for-iweth-transfer-call

Proof of Concept

  1. Navigate to https://github.com/code-423n4/2021-10-mochi/blob/main/projects/mochi-core/contracts/liquidator/DutchAuctionLiquidator.sol#L107 contract.
  2. Transfer and TransferFrom function is used instead of safe transfer on the following lines.
function settleLiquidation( uint256 _auctionId, uint256 _collateral, uint256 _repaid ) internal { Auction storage auction = auctions[_auctionId]; require(auction.boughtAt == 0, "liquidated"); IMochiVault vault = IMochiVault(auction.vault); //repay the debt first engine.usdm().transferFrom(msg.sender, address(this), _repaid); engine.usdm().burn(_repaid); IERC20 asset = vault.asset(); auction.boughtAt = block.number; asset.transfer(msg.sender, _collateral); //transfer liquidation fee to feePool uint256 liquidationFee = currentLiquidationFee(_auctionId); engine.usdm().transferFrom( msg.sender, address(engine.feePool()), liquidationFee ); emit Settled(_auctionId, _repaid + liquidationFee); }

Tools Used

Code Review

Consider using safeTransfer/safeTransferFrom or require() consistently.

#0 - r2moon

2021-10-27T13:25:19Z

Findings Information

🌟 Selected for report: nikitastupin

Also found by: WatchPug, cmichel, defsec, leastwood

Labels

bug
duplicate
2 (Med Risk)

Awards

0.0383 ETH - $159.24

External Links

Handle

defsec

Vulnerability details

Impact

The getPrice function in the contract ChainlinkAdapter.sol fetches the asset price from a Chainlink aggregator using the latestRoundData function. However, there are no checks on roundID nor timeStamp, resulting in stale prices. Stale prices could put funds at risk. According to Chainlink's documentation, This function does not error if no answer has been reached but returns 0, causing an incorrect price fed to the PriceOracle. The external Chainlink oracle, which provides index price information to the system, introduces risk inherent to any dependency on third-party data sources. For example, the oracle could fall behind or otherwise fail to be maintained, resulting in outdated data being fed to the index price calculations of the AMM. Oracle reliance has historically resulted in crippled on-chain systems, and complications that lead to these outcomes can arise from things as simple as network congestion.

Proof of Concept

  1. Navigate to "https://github.com/code-423n4/2021-10-mochi/blob/main/projects/mochi-cssr/contracts/adapter/ChainlinkAdapter.sol#L49". 2. getPrice function has been used in the repository.
  2. The following code does not check return value for the aggregator.
  3. The sample can be seen below.
(, int256 price, , , ) = feed[_asset].latestRoundData(); uint256 decimalSum = feed[_asset].decimals() + IERC20Metadata(_asset).decimals();

Tools Used

Code Review

Consider to add checks on the return data with proper revert messages if the price is stale or the round is incomplete, for example:

(uint80 roundID, int256 price, , uint256 timeStamp, uint80 answeredInRound) = ETH_CHAINLINK.latestRoundData(); require(price > 0, "Chainlink price <= 0"); require(answeredInRound >= roundID, "..."); require(timeStamp != 0, "...");

#0 - r2moon

2021-10-27T13:19:46Z

Findings Information

🌟 Selected for report: defsec

Also found by: pants

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

0.0437 ETH - $182.03

External Links

Handle

defsec

Vulnerability details

Impact

All contract initializers were missing access controls, allowing any user to initialize the contract. By front-running the contract deployers to initialize the contract, the incorrect parameters may be supplied, leaving the contract needing to be redeployed.

Proof of Concept

  1. Navigate to the following contracts.
https://github.com/code-423n4/2021-10-mochi/blob/main/projects/mochi-core/contracts/vault/MochiVault.sol#L60 https://github.com/code-423n4/2021-10-mochi/blob/main/projects/mochi-core/contracts/treasury/MochiTreasuryV0.sol#L44
  1. initialize functions does not have access control. They are vulnerable to front-running.

Tools Used

Manual Code Review

While the code that can be run in contract constructors is limited, setting the owner in the contract's constructor to the msg.sender and adding the onlyOwner modifier to all initializers would be a sufficient level of access control.

#0 - r2moon

2021-10-27T13:30:35Z

i think we don't need to fix them.

Findings Information

🌟 Selected for report: loop

Also found by: WatchPug, defsec, gzeon, harleythedog

Labels

bug
duplicate
G (Gas Optimization)

Awards

0.0101 ETH - $41.93

External Links

Handle

defsec

Vulnerability details

Impact

The liquidated variable is redundant. The variable should be deleted for the gas optimization if It is not used.

Proof of Concept

Tools Used

None

Consider removing the unused function which is not used.

#0 - r2moon

2021-10-27T13:32:27Z

Findings Information

🌟 Selected for report: defsec

Labels

bug
G (Gas Optimization)

Awards

0.0768 ETH - $319.56

External Links

Handle

defsec

Vulnerability details

Impact

Using newer compiler versions and the optimizer gives gas optimizations and additional safety checks are available for free.

The advantages of versions 0.8.* over <0.8.0 are:

  • Safemath by default from 0.8.0 (can be more gas efficient than library based safemath.)
  • Low level inliner : from 0.8.2, leads to cheaper runtime gas. Especially relevant when the contract has small functions. For example, OpenZeppelin libraries typically have a lot of small helper functions and if they are not inlined, they cost an additional 20 to 40 gas because of 2 extra jump instructions and additional stack operations needed for function calls.
  • Optimizer improvements in packed structs: Before 0.8.3, storing packed structs, in some cases used an additional storage read operation. After EIP-2929, if the slot was already cold, this means unnecessary stack operations and extra deploy time costs. However, if the slot was already warm, this means additional cost of 100 gas alongside the same unnecessary stack operations and extra deploy time costs.
  • Custom errors from 0.8.4, leads to cheaper deploy time cost and run time cost. Note: the run time cost is only relevant when the revert condition is met. In short, replace revert strings by custom errors.

Proof of Concept

  1. The contest repository contracts contain pragma 0.8.0^. The contracts pragma version should be updated to 0.8.4. (https://github.com/code-423n4/2021-10-mochi)

Tools Used

None

Consider to upgrade pragma to at least 0.8.4.

#0 - r2moon

2021-10-27T13:23:47Z

Could you share any links related with that?

#1 - r2moon

2021-10-27T13:24:11Z

Sorry, i clicked wrong button, so closed, but not it has been reopend ;)

Findings Information

🌟 Selected for report: defsec

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

0.0768 ETH - $319.56

External Links

Handle

defsec

Vulnerability details

Impact

This does not directly impact the smart contract in anyway besides cost. This is a gas optimization to reduce cost of smart contract. Calling each function, we can see that the public function uses 496 gas, while the external function uses only 261.

Proof of Concept

According to Slither Analyzer documentation (https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external), there are functions in the contract that are never called. These functions should be declared as external in order to save gas.

Slither Detector:

external-function:

https://github.com/code-423n4/2021-10-mochi/blob/main/projects/mochi-core/contracts/vault/MochiVault.sol#L75

Tools Used

Slither

  1. Clone repository for Mochi Smart Contracts.
  2. Create a python virtual environment with a stable python version.
  3. Install Slither Analyzer on the python VEM.
  4. Run Slither against all contracts.
  5. Define public functions as an external for the gas optimization.
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