# Can access cards of other markets

RCMarket.ownerOf/onlyTokenOwner lacked a _cardId < numberOfCards bound, letting upgradeCard reference another market's NFT and steal it.

- Payout: Audit contest
- Severity: high
- Program: Reality Cards
- Found by: gpersoon
- Disclosed: 2021-08-02
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-06-realitycards-findings/issues/11
- Library page: https://bugbountylibrary.com/r/c4%3A2021-06-realitycards%3AH-02
- Tags: area:web3 domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** RCMarket.sol NFT market contract (Solidity)
**Root cause:** ownerOf and onlyTokenOwner compute _tokenId = _cardId + totalNftMintCount without verifying _cardId < numberOfCards, so out-of-range card IDs resolve to NFTs belonging to other markets.
**Steps:**
1. Call `upgradeCard(_card)` on an approved market with `_card` >= numberOfCards of that market.
2. onlyTokenOwner passes because ownerOf(_card) resolves to the caller's NFT in another market.
3. upgradeCard computes `_tokenId = _card + totalNftMintCount`, transfers the foreign card in via `_transferCard`, then `nfthub.withdrawWithMetadata(_tokenId)` burns/withdraws it.
4. isMarketApproved check is bypassed by routing through an approved market while the card originates from an unapproved one.
**How found:** Code review of ownerOf/onlyTokenOwner noticed no upper bound on _cardId relative to numberOfCards, then traced upgradeCard's arithmetic to see it could address another market's token.
**Exploitation:** Attacker owning a card in an unapproved market calls upgradeCard on an approved market with an out-of-range _card index; the contract becomes the final resting place of the foreign NFT and withdraws it, effectively stealing the asset.
**Escalation:** Bypasses the isMarketApproved gate by using an approved market as the entry point for cards from unapproved markets.
**Insight:** When token IDs are derived by offsetting a local index with a global counter (totalNftMintCount), always bound the local index; unbounded indices silently alias into other tenants' token ranges.

