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: 6/38
Findings: 4
Award: $1,593.53
🌟 Selected for report: 1
🚀 Solo Findings: 0
103.9584 USDC - $103.96
https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169
incrementWindow
can be called by anyone multiple times.https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L149-L169
function incrementWindow(uint256 royaltyAmount) public returns (bool) { uint256 wethBalance; require( IRoyaltyVault(msg.sender).supportsInterface(IID_IROYALTY), "Royalty Vault not supported" ); require( IRoyaltyVault(msg.sender).getSplitter() == address(this), "Unauthorised to increment window" ); wethBalance = IERC20(splitAsset).balanceOf(address(this)); require(wethBalance >= royaltyAmount, "Insufficient funds"); require(royaltyAmount > 0, "No additional funds for window"); balanceForWindow.push(royaltyAmount); currentWindow += 1; emit WindowIncremented(currentWindow, royaltyAmount); return true; }
Splitter
and call increateWindow
. Now currentWindow
is 1. Bob get 10 percent of alice's deposit. Bob call increateWindow
with royaltyAmount == 10e18
9 times. Now currentWindow
is 10. Finally, bob call claimForAllWindows
and get 10e18 weth.https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L35-L62
function claimForAllWindows( uint256 percentageAllocation, bytes32[] calldata merkleProof ) external { // Make sure that the user has this allocation granted. require( verifyProof( merkleProof, merkleRoot, getNode(msg.sender, percentageAllocation) ), "Invalid proof" ); uint256 amount = 0; for (uint256 i = 0; i < currentWindow; i++) { if (!isClaimed(msg.sender, i)) { setClaimed(msg.sender, i); amount += scaleAmountByPercentage( balanceForWindow[i], percentageAllocation ); } } transferSplitAsset(msg.sender, amount); }
If the balance is not change, the currentWindow
is not suppposed to change.
Consider introduce a variable tracking token deposit in current window and reset this variable in new window.
#0 - sofianeOuafir
2022-04-15T12:45:21Z
duplicate of #21
1207.507 USDC - $1,207.51
https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L44 https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol#L52 https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L236
A simple POC: https://gist.github.com/wuwe1/9eb5bf9e4b3f31c8db52f4fa7fac5b13
royalty-vault/contracts/RoyaltyVault.sol 44: IERC20(royaltyAsset).transfer(splitterProxy, splitterShare) == true, 52: IERC20(royaltyAsset).transfer( splits/contracts/Splitter.sol 236: didSucceed = IERC20(splitAsset).transfer(to, value);
Fund sent directly to these contract will locked due to this incompatibility.
Consider using SafeERC20
#0 - sofianeOuafir
2022-04-14T18:36:16Z
duplicate of #52
#1 - deluca-mike
2022-06-11T22:03:17Z
Actually a duplicate of #83
🌟 Selected for report: wuwe1
Also found by: defsec, kirk-baird
217.3513 USDC - $217.35
This is dangerous in scam senario because the malicious user can frontrun and become the owner of the collection. As owner, one can withdraw paymentToken
. (note that _collections.isForSale can be change by frontrunner)
createProject
.function createProject( string memory _projectId, Collection[] memory _collections ) external onlyAvailableProject(_projectId) { require( _collections.length > 0, 'CoreFactory: should have more at least one collection' );
Two way to mitigate.
msg.sender
. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol#L102#0 - sofianeOuafir
2022-04-14T22:56:37Z
This is an issue and we intend to fix it!
#1 - deluca-mike
2022-04-22T04:03:48Z
The solutions listed in #34 and #35 are better.
64.7138 USDC - $64.71
https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L156-L159
require( IRoyaltyVault(msg.sender).getSplitter() == address(this), "Unauthorised to increment window" );
The authorization check in incrementWindow
can be easily passed. However, incrementWindow
can change the value of currentWindow
which is used for loop. It should be guard properly.
https://github.com/code-423n4/2022-03-joyn/blob/main/splits/contracts/Splitter.sol#L50
for (uint256 i = 0; i < currentWindow; i++) {
Consider using white list for this task.
#0 - sofianeOuafir
2022-04-14T19:35:52Z
In my opinion, the severity level should be 3 (High Risk) instead of 2 (Med Risk) duplicate of #3
#1 - deluca-mike
2022-06-11T22:15:38Z
Converting to QA report regarding: