Reality Cards contest - 0xRajeev's results

The world's first 'outcome ownership' prediction market.

General Information

Platform: Code4rena

Start Date: 10/06/2021

Pot Size: $45,000 USDC

Total HM: 21

Participants: 12

Period: 7 days

Judge: LSDan

Total Solo HM: 13

Id: 13

League: ETH

Reality Cards

Findings Distribution

Researcher Performance

Rank: 1/12

Findings: 12

Award: $14,325.87

🌟 Selected for report: 22

πŸš€ Solo Findings: 5

Findings Information

🌟 Selected for report: pauliax

Also found by: 0xRajeev, cmichel, shw

Labels

bug
duplicate
3 (High Risk)

Awards

791.0609 USDC - $791.06

External Links

Handle

0xRajeev

Vulnerability details

Impact

The Market sponsor(address _sponsorAddress, uint256 _amount) function is an externally callable function that is specified to be callable only by the Factory contract during market creation, as documented in the Natspec @dev comment: "called by Factory during market creation.” However, there is no access control on the caller address as done in other functions.

Impact: Anyone can call with any _sponsorAddress that has given approval to treasury contract (i.e. all users across all markets who have made deposits) and make them lose that amount towards the market pot. It is not clear if an attacker could selectively do this for markets when they are winning to benefit from such funds sponsored towards the market.

Proof of Concept

Exploit Scenario: User Alice has deposited 10000 tokens to be used for different prediction markets and thereby has given Treasury contract the required allowance to spend this deposit. Evil Eve calls Market::sponsor(Alice’s address, 10,000) and contributes Alice’s deposit amount towards the market pot liquidity which cannot be won back. Alice loses all her deposit to the market which may possibly benefit Eve.

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L814-L822

Tools Used

Manual Analysis

Add require(msgSender() == factory.owner(), "Not authorised”); to Market::sponsor().

#0 - Splidge

2021-06-17T13:53:59Z

Duplicate of #40

#1 - dmvt

2021-07-11T12:39:47Z

duplicate of #40

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: a_delamo, gpersoon

Labels

bug
2 (Med Risk)

Awards

351.5826 USDC - $351.58

External Links

Handle

0xRajeev

Vulnerability details

Impact

As specified, uberOwners of Factory, Orderbook and Treasury have the highest privileges in the system because they can upgrade contracts of market, Nfthub, order book, treasury, token and factory which form the critical components of the protocol.

The contracts allow for uberOwners to be changed to a different address from the contract owner/deployer using the changeUberOwner() function which is callable by the current uberOwner. While this function checks for zero-address, there is no validation of the new address being correct. If the current uberOwner incorrectly uses an invalid address for which they do not have the private key, then the system gets locked because the uberOwner cannot be corrected and none of the other functions that require uberOwner caller can be executed.

Impact: The current uberOwner uses a non-zero but incorrect address as the new uberOwner. This gets set and now the system is locked and none of the uberOwner-only callable functions are callable. This error cannot be fixed either and will require redeployment of contracts which will mean that all existing markets have to be terminated. The system will have to be shut and restarted completely from scratch which will take a reputation hit and have a serious technical and business impact.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards#mortar_board-governance-mortar_board

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCFactory.sol#L444-L449

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L117-L121

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L264-L268

Tools Used

Manual Analysis

Change the single-step change of uberOwner address to a two-step process where the current uberOwner first approves a new address as a pendingUberOwner. That pendingUberOwner has to then claim the ownership in a separate transaction which cannot be done if they do not have the correct private key. An incorrectly set pendingUberOwner can be reset by changing it again to the correct one who can then successfully claim it in the second step.

#0 - Splidge

2021-06-17T14:41:59Z

Duplicate of #5

#1 - dmvt

2021-07-09T13:28:20Z

There is a very low probability coupled with a very high impact, making this a Medium risk issue in my opinion.

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: gpersoon, pauliax

Labels

bug
2 (Med Risk)
disagree with severity

Awards

351.5826 USDC - $351.58

External Links

Handle

0xRajeev

Vulnerability details

Impact

The balancedBooks modifier is used to β€œcheck that funds haven't gone missing during this function call” and is applied to deposit, withdrawDeposit, payRent, payout and sponsor Treasury functions which move funds in and out of the Treasury or adjust its market/user balances.

However, this modifier is missing on refundUser() and topupMarketBalance() functions which also perform similar actions.

Impact: Any miscalculations in these functions will lead to the system becoming insolvent.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L123-L132

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L447-L450

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L372

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L282

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L325

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L409

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L432

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L469

Tools Used

Manual Analysis

Add modifier to the two functions above where it is missing.

#0 - Splidge

2021-06-18T12:34:23Z

Duplicate of #23

Findings Information

🌟 Selected for report: jvaqa

Also found by: 0xRajeev, pauliax, shw

Labels

bug
duplicate
2 (Med Risk)

Awards

237.3183 USDC - $237.32

External Links

Handle

0xRajeev

Vulnerability details

Impact

Three timestamps are used during market creation to indicate market opening, locking and oracle resolution times. The validation of these timestamps is performed in RCFactory during market creation.

The validation for market locking _timestamp[1] is performed only if maximumDuration is != 0 where the default value is 0 and is set only if setMaximumDuration() is called. Even in that validation, the use of block.timestamp should really be _timestamps[0] (market opening timestamp) because the market opening-locking duration should be counted from _timestamp[0] and not block.timestamp at createMarket() time. _timestamp[0] could be in the past. This check also doesn't account for advancedWarning aspect of market opening.

Impact: Incorrect/Insufficient validations of all possible market opening/locking configurations could affect user interactions with the market by breaking assumed invariants in the code.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCFactory.sol#L530-L535

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCFactory.sol#L519-L528

Tools Used

Manual Analysis

Specify all possible market opening/locking invariants and validate correctly against all variations of them.

#0 - Splidge

2021-06-18T10:17:14Z

Duplicate of #61

#1 - dmvt

2021-07-10T13:44:03Z

duplicate of #61

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor confirmed
resolved

Awards

1302.1578 USDC - $1,302.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

rentAllCards() requires the sender to specify a _maxSumOfPrices parameter which specifies β€œlimit to the sum of the bids to place” as specified in the Natspec @param comment. This is apparently for front-run protection.

However, this function parameter constraint for _maxSumOfPrices is broken in the function implementation which leads to the total of bids places greater than the _maxSumOfPrices specified.

Impact: The user may not have sufficient deposit, be foreclosed and/or impacted on other bids/markets.

Proof of Concept

Scenario: Assume two cards for a market with current winning rentals of 50 each. _maxSumofPrices = 101 passes check on L643 but then the forced 10% increase on L650 (assuming sender is not the owner of either card) causes newRentals to be called with 55 for each card thus totalling to 110 which is > 101 as requested by the user.

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L636-L637

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L639-L657

Tools Used

Manual Analysis

Modify the max sum of prices check logic to consider the 10% increase scenarios. Document and suggest the max sum of prices for the user in the UI based on the card prices and 10% requirement depending on card ownership.

#0 - Splidge

2021-06-21T11:11:36Z

fixed here

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor disputed
disagree with severity

Awards

1302.1578 USDC - $1,302.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

Ability to pause all token transfers and all state changes for contracts is a β€œguarded-launch” best-practice for emergency situations for newly launched projects. The project implements this using a marketsPaused flag per market and a globalPause flag across all markets.

While these prevent renting of cards in a specific market and deposit/withdraw/rent cards across all markets, there are still public/external functions that are unguarded by these flags which can affect contract state in paused scenarios that will make it hard/impossible to recover correctly from the emergency pause.

Examples: topupMarketBalance() and refundUser() in Treasury can be triggered even in a globalPause scenario. There could be other function flows where it is not obvious that market/global pausing is enabled because it is enforced in one of the functions called deep within the code within one of the conditionals.

Impact: Markets get paused but the contracts cannot be restarted because of state changes affected during the pause via unguarded external/public functions.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L370-L380

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L446-L463

Tools Used

Manual Analysis

Apply marketPaused and globalPause check clearly in the beginning of all public/external functions which move tokens/funds in/out or change contract state in any way.

Validate all possible control flows to check that market/global pausing works in all scenarios and applies to all contract state and not specific functionalities.

#0 - Splidge

2021-06-18T11:58:27Z

marketPause is declared as only limiting rentals in a specific market. globalPause is declared as stopping deposits, withdraws and rentals across all markets. Therefore they are functioning as intended.

Also, the example of refundUser() is not true, it will fail in a globalPause because it is only called by markets during a rent collection and a rent collection requires the calling of payout which is restricted by globalPause.

#1 - dmvt

2021-07-10T16:56:43Z

topupMarketBalance does appear to be a deposit of sorts. I think the warden's take on the issue is valid and sponsor should seriously consider looking closer at the potential side effects of not fully pausing intentional transfer functions.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor disputed
disagree with severity

Awards

1302.1578 USDC - $1,302.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

The Treasury deposit() function credits amount to the user address in parameter instead of the msgSender() that is actually making the deposit whose rationale as explained in the Natspec comment is that this may be called via contract or L1-L2 bot.

However, the deposit whitelist should ideally be enforced on the _user address. If msgSender() is blacklisted, user address can still deposit() from another whitelisted msgSender() address while retaining the user address that is used for leader boards and NFTs.

Impact: User misbehaves in interactions with the system (e.g. trolls, spams) and their corresponding msgSender() is removed from the whitelist. User continues to deposit into the system via another whitelisted msgSender() without any impact to leader boards or NFTs.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L70-L71

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L204-L221

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L279-L297

Tools Used

Manual Analysis

Use whitelist on user address instead of msgSender().

#0 - Splidge

2021-06-18T13:34:56Z

It is stated that the whitelist will "only allow certain addresses to deposit" here and that toggleWhitelist() allows an address to deposit here.

I think that the whitelist is performing as intended, but thanks for this issue report as this could easily have been a larger issue.

We only plan to use the whitelist as a very rudimentary barrier just for the initial launch. I think that only allowing certain addresses to deposit is sufficient for now. Maybe if time allows I'll make the changes but changing the whitelist to allow the _user instead of the msgSender() would also block contracts and layer1->layer2 bot, so there'd need to be exceptions made for them. I'd rather not play about with sensitive functions at the last minute when we aren't going to be using the whitelist much anyway.

#1 - dmvt

2021-07-10T17:28:04Z

Warden makes a good point. This could allow griefing of other parts of the system. If the barrier winds up being needed longer than expected or users act in unexpected ways, sponsor may wind up wishing they had reconsidered addressing this. Obviously, sponsor is free to ignore, but the issue seems to be a valid one with significant potential impact.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor confirmed
disagree with severity
resolved

Awards

1302.1578 USDC - $1,302.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

Orderbook.removeBids() as commented β€œ///remove bids in closed markets for a given user ///this can reduce the users bidRate and chance to foreclose”

removeOldBids() is performed currently in Market.newRental() and Treasury.deposit() to β€œdo some cleaning up, it might help cancel their foreclosure” as commented. However, this is missing in the withdrawDeposit() function where the need is the most because user is removing deposit which may lead to foreclosure and is even commented as being useful on L356.

Impact: If we do not remove closed market bids during withdrawDeposit, the closed market bids still get accounted in user's bidrate in the conditional on L357 and therefore do not prevent the foreclosure in withdrawDeposit that may happen in L357-L367. User may get foreclosed because of mis-accounted closed-market bids in the order book.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L671-L713

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L356

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L357-L367

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L704-L705

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L300-L301

Tools Used

Manual Analysis

Add call to removeOldBids() on L355 of withdrawDeposit() of Treasury.

#0 - Splidge

2021-06-21T11:32:22Z

This was intentionally left out in an older version of the contracts because of the way withdrawDeposit worked before we had the per-user rent collection. Added it back in again here.

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
2 (Med Risk)
sponsor confirmed
resolved

Awards

1302.1578 USDC - $1,302.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

ERC721 standard and implementation allows the use of approved addresses to affect transfers besides the token owners. However, the L2 NFT Hub implementation deviates from ERC721 by ignoring the presence of any approvers in the overriding function implementations of transferFrom() and safeTransferFrom().

Impact: The system interactions with NFT platforms may not work if they expect ERC721 adherence. Users who interact via approved addresses will see their transfers failing for their approved addresses.

Given that the key value proposition of this project is the use of NFTs, the expectation will be that it is fully compatible with ERC721.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/nfthubs/RCNftHubL2.sol#L212-L234

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L158

Tools Used

Manual Analysis

Add support for approval in NFT transfers.

#0 - mcplums

2021-06-17T07:17:50Z

This is a nice one, I see no reason why we can't implement this

#1 - Splidge

2021-06-18T09:23:01Z

Yes, we will need to add this, although we will need to override the approvals until the market has locked and the cards true owner is discovered.

#2 - Splidge

2021-06-21T13:07:57Z

I've changed from overriding specific functions which could be dangerous if we were to upgrade to an OpenZeppelin implementation that had alternative transfer functions. Now we use the _beforeTokenTransfer hook and check that only the factory or the market can do a transfer before the market has entered the withdraw state. Implemented here

Findings Information

🌟 Selected for report: cmichel

Also found by: 0xRajeev

Labels

bug
duplicate
2 (Med Risk)

Awards

585.971 USDC - $585.97

External Links

Handle

0xRajeev

Vulnerability details

Impact

ERC721 specification for safeTransferFrom says: β€œthis function checks if _to is a smart contract (code size > 0). If so, it calls onERC721Received on _to and throws if the return value is not bytes4(keccak256(β€œonERC721Received(address,address,uint256,bytes)”)).” This is implemented by the widely-used OpenZeppelin implementation which is also inherited here.

However, the overriding implementation in L2 NFT Hub is missing the check implemented by β€œrequire(_checkOnERC721Received(..))” after _transfer() to evaluate if the destination contract was indeed ERC-721 compatible.

Impact: NFT is transferred to a non ERC-721 compatible contract and gets locked/lost.

Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/nfthubs/RCNftHubL2.sol#L223-L234

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L211-L212

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L174-L213

https://eips.ethereum.org/EIPS/eip-721

Tools Used

Manual Analysis

Add require(_checkOnERC721Received(..))” after _transfer() on L232.

#0 - Splidge

2021-06-18T10:39:17Z

Duplicate of #160

#1 - dmvt

2021-07-11T11:03:36Z

duplicate of #160

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