Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $75,000 USDC
Total HM: 27
Participants: 144
Period: 7 days
Judge: gzeon
Total Solo HM: 13
Id: 170
League: ETH
Rank: 19/144
Findings: 3
Award: $648.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Trust
Also found by: csanuragjain
585.87 USDC - $585.87
User can call bondUtilityToken function with large pod value which ensure pushing [address(0)] from _operatorPods.length till pod.
This becomes a problem _operatorPods.length increases dramatically causing randomess to reduce significantely in crossChainMessage. Reason being random % _operatorPods.length will mostly result in unused pod with address(0) as only operator, causing open season where anyone can execute the job
function bondUtilityToken( address operator, uint256 amount, uint256 pod ) external { ... for (uint256 i = _operatorPods.length; i <= pod; i++) { /** * @dev add zero address into pod to mitigate empty pod issues */ _operatorPods.push([address(0)]); } ... }
Lets say _operatorPods initially had 2 elements so after calling this function and pod as 100 this will push address(0) 98 times making the _operatorPods length as 100
This becomes a problem while calling crossChainMessage function
function crossChainMessage(bytes calldata bridgeInRequestPayload) external payable { ... uint256 pod = random % _operatorPods.length; uint256 podSize = _operatorPods[pod].length; uint256 operatorIndex = random % podSize; ... }
Assume random is selected as 10, which makes pod as 10 and _operatorPods[pod].length is 1 (while initializing one dummy operator with address 0 is added). This makes operatorIndex as 0 (random%1=0)
Now 0 operatorIndex means open season and anyone can execute the job
/** * @dev If operator index is 0, then it's open season! Anyone can execute this job. First come first serve * pop operator to ensure that they cannot be selected for any other job until this one completes * decrease pod size to accomodate popped operator */
Do not allow user to specify a very large value for pods
#0 - gzeoneth
2022-10-31T15:57:25Z
As noted in #256, this seems to be bounded since the cost of joining higher pod increase exponentially
#1 - alexanderattar
2022-11-08T22:08:39Z
Design decision. Operator that bonds massive amounts of tokens to a higher than next pod is purposefully punished by decreasing their probability of exclusive job rights. Forces a gradual bonding curve
#2 - gzeoneth
2022-11-19T17:43:38Z
#432 as primary
In case if a non operator calls the executeJob, then the slashed amount added to this non operator cannot be redeemed as shown in poc causing loss of funds
Operator A forgets to execute job
User A sees an opportunity and immediately calls the executeJob function
This slashes the _getBaseBondAmount from Operator A and add this to User A
uint256 amount = _getBaseBondAmount(pod); /** * @dev select operator that failed to do the job, is slashed the pod base fee */ _bondedAmounts[job.operator] -= amount; /** * @dev the slashed amount is sent to current operator */ _bondedAmounts[msg.sender] += amount;
Now User A has _bondedAmounts[User A]=amount but _bondedOperators[User A] remain 0 since User A is not an operator
This becomes a problem since now User cannot redeem the funds using unbondUtilityToken function since below condition will fail
require(_bondedOperators[operator] != 0, "HOLOGRAPH: operator not bonded");
Add a check to verify that caller is a valid operator
require(_bondedOperators[msg.sender] != 0, "HOLOGRAPH: operator not bonded");
#0 - gzeoneth
2022-10-30T16:29:53Z
Duplicate of #322
🌟 Selected for report: Rolezn
Also found by: 0x1f8b, 0x52, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xhunter, 0xzh, 8olidity, Amithuddar, Aymen0909, B2, Bnke0x0, Chom, Deivitto, Diana, Diraco, Dravee, Franfran, JC, Jeiwan, Josiah, JrNet, Jujic, KingNFT, KoKo, Lambda, Margaret, Migue, Ocean_Sky, PaludoX0, Picodes, Rahoz, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Shinchan, Tagir2003, Trust, Waze, Yiko, __141345__, a12jmx, adriro, ajtra, arcoun, aysha, ballx, bin2chen, bobirichman, brgltd, bulej93, catchup, catwhiskeys, caventa, cccz, cdahlheimer, ch0bu, chaduke, chrisdior4, cloudjunky, cryptostellar5, cryptphi, csanuragjain, cylzxje, d3e4, delfin454000, djxploit, durianSausage, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, hansfriese, i_got_hacked, ignacio, imare, karanctf, kv, leosathya, louhk, lukris02, lyncurion, m_Rassska, malinariy, martin, mcwildy, mics, minhtrng, nicobevi, oyc_109, pashov, peanuts, pedr02b2, peiw, rbserver, ret2basic, rotcivegaf, rvierdiiev, ryshaw, sakman, sakshamguruji, saneryee, securerodd, seyni, sikorico, svskaushik, teawaterwire, tnevler, w0Lfrum
55.6726 USDC - $55.67
Contract: https://github.com/holographxyz/holograph-protocol/blob/c4_audit/contracts/abstract/ERC20H.sol#L140 https://github.com/holographxyz/holograph-protocol/blob/c4_audit/contracts/abstract/ERC721H.sol#L140
Issue: It was observed that init function can be called by any user bricking the contract owner. This will force the contract owner to redeploy this contract causing wastage of gas
Recommendation: Only the owner (decided via constructor) should be allowed to call init function. This need to be fixed for almost all contracts