Nested Finance contest - GreyArt's results

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

General Information

Platform: Code4rena

Start Date: 11/11/2021

Pot Size: $50,000 USDC

Total HM: 9

Participants: 27

Period: 7 days

Judge: alcueca

Total Solo HM: 5

Id: 53

League: ETH

Nested Finance

Findings Distribution

Researcher Performance

Rank: 1/27

Findings: 7

Award: $11,095.85

🌟 Selected for report: 14

🚀 Solo Findings: 2

Findings Information

🌟 Selected for report: GreyArt

Also found by: hack3r-0m

Labels

bug
2 (Med Risk)

Awards

1169.9215 USDC - $1,169.92

External Links

Handle

GreyArt

Vulnerability details

Impact

It is possible for duplicate shareholders to be added. These shareholders will get more than intended when _sendFee() is called.

Ensure that the _accounts array is sorted in setShareholders().

for (uint256 i = 0; i < _accounts.length; i++) {
	if (i > 0) {
		require(_accounts[i - 1] < _accounts[i], "FeeSplitter: ACCOUNTS_NOT_SORTED");
	}
	_addShareholder(_accounts[i], _weights[i]);
}

#0 - adrien-supizet

2021-11-22T10:01:05Z

Duplicate #231

#1 - adrien-supizet

2021-11-22T10:02:08Z

Indeed there is a fix to do here, we'll prevent adding the same shareholders instead as suggested in #231

#2 - alcueca

2021-12-03T09:35:38Z

Taking this issue as the principal, and raising #231 to medium severity.

Findings Information

🌟 Selected for report: GreyArt

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

2599.8256 USDC - $2,599.83

External Links

Handle

GreyArt

Vulnerability details

Impact

A user that mistakenly calls either create() or addToken() with WETH (or another ERC20) as the input token, but includes native ETH with the function call will have his native ETH permanently locked in the contract.

It is best to ensure that msg.value = 0 in _transferInputTokens() for the scenario mentioned above.

} else if (address(_inputToken) == ETH) {
	...
} else {
	require(msg.value == 0, "NestedFactory::_transferInputTokens: ETH sent for non-ETH transfer");
  _inputToken.safeTransferFrom(_msgSender(), address(this), _inputTokenAmount);
}

Findings Information

🌟 Selected for report: GreyArt

Labels

bug
2 (Med Risk)
sponsor acknowledged

Awards

2599.8256 USDC - $2,599.83

External Links

Handle

GreyArt

Vulnerability details

Impact

There is no limit to the number of shareholders. It is therefore possible to set a large number of shareholders such that _sendFees() will run out of gas when adding shares to each shareholder. This will cause denial of service to all NestedFactory functions, especially the ones that will remove funds like withdraw() and destroy().

It would be best to set a sanity maximum number of shareholders that can be added.

Findings Information

🌟 Selected for report: GreyArt

Also found by: WatchPug

Labels

bug
2 (Med Risk)

Awards

1169.9215 USDC - $1,169.92

External Links

Handle

GreyArt

Vulnerability details

Impact

While there is no loss of funds, removing an operator will cause the cache functionality to be permanently broken. If there was a function that had a modifier which requires the cache to be synced before the function can be called, it would not be callable as well.

The underlying issue is how the bytes32 operator is removed from the array when removeOperator() is called. Its value gets deleted (set to 0x0), but isn't taken out of the array.

Proof of Concept

For ease of reading, we use abbreviated strings like 0xA, 0xB for bytes32 and address types.

  1. Import 3 operators by calling OperatorResolver.importOperators([0xA, 0xB, 0xC], [0x1, 0x2, 0x3).
  2. Call NestedFactory.addOperator() 3 times to push these 3 operators into the operators state variable.
  3. Call NestedFactory.rebuildCache() to build the cache.
  4. Let's say the second operator 0xB is to be removed. Taking reference from the removeOperator.ts script, OperatorResolver.importOperators([0xA, 0xB, 0xC], [0x1, 0x2, 0x3) is called first. This works because OperatorResolver uses a mapping(bytes32 ⇒ address) to represent the operators. Hence, by setting 0xB's destination address to be the null address, it is like as if he was never an operator.
  5. Call NestedFactory.rebuildCache() to rebuild the cache. resolverAddressesRequired() will return [0xA, 0xB, 0xC]. 0xB will be removed from addressCache because resolver.getAddress(0xB) returns 0x000 since it has been deleted from the OperatorResolver.
  6. Call NestedFactory.removeOperator(0xB). The operators array now looks like this: [0xA, 0x0, 0xC].
  7. When you try to call NestedFactory.isResolverCached, it will always return false because of the null bytes32 value, where addressCache[0x0] will always return the null address.

Instead of doing an element deletion, it should be replaced with the last element, then have the last element popped in removeOperator().

function removeOperator(bytes32 operator) external override onlyOwner {
	for (uint256 i = 0; i < operators.length; i++) {
		if (operators[i] == operator) {
			operators[i] = operators[operators.length - 1];
			operators.pop();
			break;
		}
	}
}

#0 - maximebrugel

2021-11-22T10:29:16Z

Duplicated : #58

#1 - alcueca

2021-12-03T11:10:10Z

Taking this issue apart as a non-duplicate, for finding the most severe consequence of the incorrect implementation.

Findings Information

🌟 Selected for report: GreyArt

Also found by: WatchPug

Labels

bug
2 (Med Risk)

Awards

1169.9215 USDC - $1,169.92

External Links

Handle

GreyArt

Vulnerability details

Impact

Currently, many core operations (like NestedFactory.create(), NestedFactory.swapTokenForTokens()) are dependent on the assumption that the cache is synced before these functions are executed however this may not necessarily be the case.

Proof of Concept

  1. OperatorResolver.importOperators() is called to remove an operator.
  2. A user calls NestedFactory.create() that uses the operator that was being removed / updated.
  3. NestedFactory.rebuildCache() is called to rebuild cache.

This flow is not aware that the cache is not in synced.

Add a modifier to require that the cache is synced to all functions that interact with the operators.

#0 - maximebrugel

2021-11-22T21:45:03Z

Duplicated : #157

#1 - alcueca

2021-12-03T10:22:31Z

Taking this as the main.

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