Nested Finance contest - csanuragjain's results

The one-stop Defi app to build, manage and monetize your portfolio.

General Information

Platform: Code4rena

Start Date: 10/02/2022

Pot Size: $30,000 USDC

Total HM: 5

Participants: 24

Period: 3 days

Judge: harleythedog

Total Solo HM: 3

Id: 86

League: ETH

Nested Finance

Findings Distribution

Researcher Performance

Rank: 9/24

Findings: 3

Award: $1,205.81

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: robee

Also found by: 0x1f8b, csanuragjain

Labels

bug
duplicate
2 (Med Risk)

Awards

1024.836 USDC - $1,024.84

External Links

Lines of code

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/NestedRecords.sol#L111

Vulnerability details

Impact

Duplicate tokens can be added which could exceed maxHoldingsCount and thus rejecting all functions in Factory

Proof of Concept

  1. Observe the store function in NestedRecords.sol#L111
function store( uint256 _nftId, address _token, uint256 _amount, address _reserve ) external onlyFactory { uint256 amount = records[_nftId].holdings[_token]; if (amount != 0) { require(records[_nftId].reserve == _reserve, "NRC: RESERVE_MISMATCH"); updateHoldingAmount(_nftId, _token, amount + _amount); return; } require(records[_nftId].tokens.length < maxHoldingsCount, "NRC: TOO_MANY_TOKENS"); require( _reserve != address(0) && (_reserve == records[_nftId].reserve || records[_nftId].reserve == address(0)), "NRC: INVALID_RESERVE" ); records[_nftId].holdings[_token] = _amount; records[_nftId].tokens.push(_token); records[_nftId].reserve = _reserve; }
  1. Factory calls the store function (via say _submitOrder function) with _nftId as 1, Token as X and amount as 0 (This is possible if a custom operator returns amounts[0] as 0 )

  2. Since amount is 0 so below happens:

records[1].holdings[X] = 0; records[1].tokens.push(X);
  1. Now say Step 2 happens again that is store is again called with _nftId as 1, Token as X and amount as 0.

  2. This time store mishandles the request since amount = records[_nftId].holdings[_token] =0 which means below statement will execute again

records[1].holdings[X] = 0; records[1].tokens.push(X);
  1. records[1].holdings[X] is harmless and will simply overwrite value but records[1].tokens.push(X); will add a duplicate token X which is incorrect

  2. This becomes bigger problem once token length reaches maxHoldingsCount, making the factory unusable

Make sure that token is not already existing before adding it

#1 - harleythedogC4

2022-02-27T06:31:15Z

Marking this as the main issue

#2 - harleythedogC4

2022-02-27T16:43:39Z

Upon second thought I am taking the other issue as the main issue.

Findings Information

🌟 Selected for report: pauliax

Also found by: 0xliumin, Dravee, GreyArt, IllIllI, Omik, ShippooorDAO, WatchPug, bobi, csanuragjain, gzeon, kenzo, rfa, robee, samruna

Labels

bug
QA (Quality Assurance)
sponsor confirmed

Awards

90.9726 USDC - $90.97

External Links

Missing token whitelisting puts stakeholders on risk

Contract: https://github.com/code-423n4/2022-02-nested/blob/main/contracts/FeeSplitter.sol#L146

  1. Attacker can call sendFees with a malicious token contract

  2. This increases the share balance of malicious token for each stake holder

  3. When stakeholders tries to withdraw there share of malicious token using releaseTokens, malicious contract will be called and code written by attacker will be executed (asking for unauthorized approvals, wasting Gas etc)

sendFees & sendFeesWithRoyalties not handling ETH token

Contract: https://github.com/code-423n4/2022-02-nested/blob/main/contracts/FeeSplitter.sol#L175

  1. Both sendFees & sendFeesWithRoyalties are not considering if the input _token is ETH as done in releaseTokens

Incorrect return message

Contract: https://github.com/code-423n4/2022-02-nested/blob/main/contracts/abstracts/MixinOperatorResolver.sol#L101

  1. The require statement incorrectly mentions INVALID_OUTPUT_TOKEN when it should be INVALID_INPUT_TOKEN

#0 - maximebrugel

2022-02-10T15:25:24Z

"Missing token whitelisting puts stakeholders on risk" (Disputed)

Yes and no... Yes it can run malicious code in the transfer function, but we can skip the token if we want. Same logic can be applied to NestedFactory. We can't really change that since we are not "whitelisting" tokens.

"sendFees & sendFeesWithRoyalties not handling ETH token" (Disputed)

We are only sending fees with ERC20 (so WETH and not ETH). In the releaseTokens tokens we are unwrapping the WETH to transfer ETH.

"Incorrect return message" (Confirmed)

#1 - maximebrugel

2022-02-16T13:32:56Z

#2 - harleythedogC4

2022-03-01T02:14:35Z

My personal judgements:

  1. "Missing token whitelisting puts stakeholders on risk". User's don't need to interact with tokens if they don't want to. Invalid
  2. "sendFees & sendFeesWithRoyalties not handling ETH token". As sponsor describes. Invalid.
  3. "Incorrect return message". Valid and non-critical.

#3 - harleythedogC4

2022-03-03T01:51:08Z

Also adding in the reduced severity finding #10: 4. "Royalty owner can steal Stakeholders fees". Just code consistency. Valid and non-critical.

#4 - harleythedogC4

2022-03-03T02:25:38Z

Now, here is the methodology I used for calculating a score for each QA report. I first assigned each submission to be either non-critical (1 point), very-low-critical (5 points) or low-critical (10 points), depending on how severe/useful the issue is. The score of a QA report is the sum of these points, divided by the maximum number of points achieved by a QA report. This maximum number was 26 points, achieved by https://github.com/code-423n4/2022-02-nested-findings/issues/66.

The number of points achieved by this report is 2 points. Thus the final score of this QA report is (2/26)*100 = 8.

Findings Information

Awards

90.0012 USDC - $90.00

Labels

bug
G (Gas Optimization)
sponsor confirmed

External Links

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/FeeSplitter.sol (#L191, #L149, #L166)

  1. In sendFeesWithRoyalties, releaseTokens, releaseTokensNoETH function, add check amount!=0
require(amount!=0, "Incorrect amount");

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/NestedRecords.sol#L76

  1. In updateHoldingAmount function, under the while loop, use return instead of break
if (tokens[tokenIndex] == _token) { ... return; }

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/NestedFactory.sol#L317

  1. No need to check require(batchLength != 0, "NF: INVALID_ORDERS"); as this is already checked in all the calling functions like create, _processInputOrders

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/NestedFactory.sol#L445

  1. The condition if (_amountToSpend > amounts[1]) is not required as the parent condition is already checking for require(amounts[1] <= _amountToSpend, "NestedFactory::_safeSubmitOrder: Overspent");

#0 - adrien-supizet

2022-02-15T11:07:52Z

  1. I'm not sure about the benefits of this one. To discuss
  2. This does not achieve what we want
  3. disputed, those are not the same checks
  4. confirmed

#1 - harleythedogC4

2022-03-13T02:20:00Z

My personal judgments:

  1. "add check amount!=0". I don't see why this is a gas-optimization, and there is no explanation why this is a good idea. Invalid.
  2. "use return instead of break". I disagree with the sponsor, return seems to do the same thing as break in this scenario. Valid and small optimization.
  3. "No need to check require(batchLength != 0, "NF: INVALID_ORDERS");". Agree with sponsor, these are different checks. Invalid.
  4. "The condition if (_amountToSpend > amounts[1]) is not required". Valid and small optimization.

#2 - harleythedogC4

2022-03-13T06:18:44Z

Now, here is the methodology I used for calculating a score for each gas report. I first assigned each submission to be either small-optimization (1 point), medium-optimization (5 points) or large-optimization (10 points), depending on how useful the optimization is. The score of a gas report is the sum of these points, divided by the maximum number of points achieved by a gas report. This maximum number was 10 points, achieved by #67.

The number of points achieved by this report is 2 points. Thus the final score of this gas report is (2/10)*100 = 20.

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