Platform: Code4rena
Start Date: 30/03/2022
Pot Size: $30,000 USDC
Total HM: 21
Participants: 38
Period: 3 days
Judge: Michael De Luca
Total Solo HM: 10
Id: 104
League: ETH
Rank: 11/38
Findings: 5
Award: $1,046.64
🌟 Selected for report: 0
🚀 Solo Findings: 0
103.9584 USDC - $103.96
https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169 https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L35
The Splitter
contract is used to split up the creator's proceeds. By repeatedly calling the incrementWindow()
function, a creator can steal the other's share of the proceeds.
Let's say the currentWindow
is at 0.
The Splitter
contract received 5e18
tokens that should be distributed between two parties. Both get 50% of the pot.
Alice creates a contract that fulfills the two require statements here: https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L152-L159
After the contract got the tokens, Alice calls the incrementWindow()
function using her contract twice. Creating two windows each with a pot of 5e18
: https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169
After that, Alice claims both windows using claimForAllWindows()
: https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L35
For both windows, she receives 50% of the window's pot. That's 2 * 0.5 * 5e18 == 5e18
. Thus, she takes all the funds of the Splitter contract. There's nothing left for Bob the other creator.
none
Store the balance at the time of the window creation in a state variable. When increasing the window, only assign the difference between the new and the old balance to the window's pot. Otherwise, someone can create multiple windows with the same balance.
#0 - sofianeOuafir
2022-04-15T12:44:27Z
This is a problem that we intend to fix
#1 - deluca-mike
2022-04-22T03:51:49Z
Seems like a duplicate of #3, but this has different implications and a solution that could be implemented in parallel.
#2 - deluca-mike
2022-06-11T22:27:33Z
Duplicate of #3
🌟 Selected for report: kirk-baird
724.5042 USDC - $724.50
https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L35-L62 https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169
This is somewhat related to my other submission: "Creator can steal other creator's split in Splitter contract".
But, now instead of a creator stealing the other's funds, the issue is that someone else can DOS the usage of claimForAllWindows
. Since anybody is able to call incrementWindow()
they can create so many windows that any calls to claimForAllWindows
after that will always fail.
An attacker implements a contract that calls the incrementWindow()
function: https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169
The contract they use implements the necessary functions to pass the two require functions.
They use the contract to call the incrementWindow()
function a larger number of times. After that, calls to the claimForAllWindows()
function will result in a way too high amount
value. A value that is higher than the contract's actual balance. Thus, any calls to the function will revert: https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L61
none
Limit the access to the incrementWindow()
function.
#0 - sofianeOuafir
2022-04-14T19:21:21Z
In my opinion, the severity level should be 3 (High Risk) instead of 2 (Med Risk) duplicate of #3
#1 - deluca-mike
2022-04-22T02:20:10Z
This is more a duplicate of #6, since it pertains to the possibility of claimForAllWindows
being made prohibitively expensive, regardless if it's from an attacker abusing incrementWindow
, or natural growth of currentWindow
without claims.
🌟 Selected for report: hyh
Also found by: Ruhum, WatchPug, hubble, kirk-baird, leastwood, pedroais, rayn, saian, securerodd
70.1719 USDC - $70.17
The CoreCollection.initialize()
function can be called multiple times by the creator of the collection. It is used to configure the collection. A malicious owner can abuse that by retroactively changing certain configurations. They could increase the max supply of the collection. Or, they could frontrun a user's purchase and increase the mintFee
draining the user's funds. They can also change the splitFactory
value which is normally not configurable by the owner.
function initialize( string memory _collectionName, string memory _collectionSymbol, string memory _collectionURI, uint256 _maxSupply, uint256 _mintFee, address _payableToken, bool _isForSale, address _splitFactory ) external onlyOwner onlyValidSupply(_maxSupply) { _name = _collectionName; _symbol = _collectionSymbol; _baseUri = _collectionURI; maxSupply = _maxSupply; mintFee = _mintFee; payableToken = IERC20(_payableToken); isForSale = _isForSale; splitFactory = _splitFactory; initialized = true; }
The initialize()
function only has two modifiers: onlyOwner()
and onlyValidSupply()
. The first prevents anybody but the owner from calling this function. The other enforces the maxSupply
value to be > 0
. There is no check whether the collection was already initialized.
The first initialization is done by the CoreFactory
contract here. After that, the ownership is transferred to the original caller here. Thus, the creator is able to call the initialize()
function multiple times.
Now let's say a user wants to mint 5 NFTs. The initial mintFee
is 1e18
. For whatever reason the user approves more funds to the collection than necessary for that purchase. Let's say they approve 10e18
. Maybe they want to buy more in another transaction. Who knows. The creator of the NFT collection searches the mempool for a situation like this.
It frontruns the user's transaction and calls initialize()
to set the mintFee
to 2e18
instead. Now they can pull the whole 10e18
tokens from the user's wallet here.
none
Add the onlyUnInitialized()
modifier to the initialize()
function.
#0 - sofianeOuafir
2022-04-14T19:43:28Z
duplicate of #4
🌟 Selected for report: kirk-baird
Also found by: 0xDjango, Dravee, Ruhum, TomFrenchBlockchain, WatchPug, defsec, hubble, hyh, leastwood, minhquanym
85.0569 USDC - $85.06
Judge has assessed an item in Issue #25 as High risk. The relevant finding follows:
Fees should have a boundary of 100% (10000): https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L68
Otherwise the contract will try to transfer more than possible which will result in reverts: https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L40
It might also be helpful the have an fixed upper boundary that doesn't allow the platform to collect more than a set amount of fees, e.g. 10%.
62.952 USDC - $62.95
You're ignoring the return value of an ERC20 transfer twice:
https://github.com/code-423n4/2022-03-joyn/blob/main/core-contracts/contracts/ERC721Payable.sol#L54
Either use SafeERC20 or check the return value as you do in other places in the code base.
Fees should have a boundary of 100% (10000): https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L68
Otherwise the contract will try to transfer more than possible which will result in reverts: https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L40
It might also be helpful the have an fixed upper boundary that doesn't allow the platform to collect more than a set amount of fees, e.g. 10%.
When creating a project with collections, there is no ID linking the collections to that specific project. Both entities exist independent of each other. There seems to be no way of associating them with each other after the creation (besides the emitted event).
#0 - sofianeOuafir
2022-04-14T22:25:09Z
Severity should be 3 - High risk
duplicate of #52, #9
#1 - deluca-mike
2022-04-22T08:08:51Z
"No connection between a project & collection" is a valid comment, but not necessary to fix.
#2 - deluca-mike
2022-04-22T08:14:06Z
Converted second issue to #138
#3 - deluca-mike
2022-04-22T08:15:57Z
For the 3rd item, warden is suggesting some mapping or variable that would allow correlating projects and collections without needing to rely solely on events.