Canto Identity Subprotocols contest - Ruhum's results

Subprotocols for Canto Identity Protocol.

General Information

Platform: Code4rena

Start Date: 17/03/2023

Pot Size: $36,500 USDC

Total HM: 10

Participants: 98

Period: 3 days

Judge: leastwood

Total Solo HM: 5

Id: 223

League: ETH

Canto Identity Subprotocols

Findings Distribution

Researcher Performance

Rank: 41/98

Findings: 1

Award: $39.87

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: juancito

Also found by: Chom, J4de, Ruhum, adriro, igingu, leopoldjoy, luxartvinsec, pipoca, popular00, reassor

Labels

bug
2 (Med Risk)
partial-50
duplicate-130

Awards

39.8657 USDC - $39.87

External Links

Lines of code

https://github.com/code-423n4/2023-03-canto-identity/blob/main/canto-namespace-protocol/src/Tray.sol#L162

Vulnerability details

Impact

Each tray consists of 7 tiles. Tiles are assigned randomly with keccak256(lastHash) being used as the seed. Trays will be valued differently on the secondary market depending on the rarity of their tiles. Because the seed value can be precomputed, it's possible to determine which tokenId will produce a valuable tray. Bots will compete to be the ones to mint those valuable trays.

That will cause tons of failed txs & wasted gas on the Canto network. Normal users likely won't be able to mint any really valuable tray.

Generally, Pardadigm's A Guide to Designing Effective NFT Launches is worth a read.

One of the key goals of a good launch is:

Unexploitable fairness: Launches must have true randomness to ensure that predatory users cannot snipe the rarest items at the expense of less sophisticated users.

That's not the case here. Users with technical knowledge will be able to snipe all the rare trays while less-technical users will be stuck with the invaluable ones.

Proof of Concept

For any given tray, the tile data is computed using the lastHash as the seed:

        for (uint256 i; i < _amount; ++i) {
            TileData[TILES_PER_TRAY] memory trayTiledata;
            for (uint256 j; j < TILES_PER_TRAY; ++j) {
                lastHash = keccak256(abi.encode(lastHash));
                trayTiledata[j] = _drawing(uint256(lastHash));
            }
            tiles[startingTrayId + i] = trayTiledata;
        }

Because lastHash doesn't depend on the caller, anybody is able to mint the valuable tray as long as they are the ones whose transaction succeeds.

Tools Used

none

Instead of only using lastHash as the seed, incorporate the caller-specific value, e.g. msg.sender so that lastHash = keccak256(lastHash, msg.sender). That way, the value of any token depends on both lastHash & the caller. So while token 5 might be valuable for Alice because of her address it might not be for Bob. They won't compete for it.

While this allows people to search for "good" addresses to mint trays with, the time window in which they can use that address is limited. As soon as a new tray is minted, lastHash changes. Their address will be worthless after that.

#0 - c4-judge

2023-03-28T18:49:35Z

0xleastwood marked the issue as duplicate of #121

#1 - c4-judge

2023-04-11T19:55:06Z

0xleastwood marked the issue as satisfactory

#2 - c4-judge

2023-04-11T20:03:18Z

0xleastwood marked the issue as duplicate of #121

#3 - c4-judge

2023-04-12T00:54:21Z

0xleastwood marked the issue as duplicate of #130

#4 - c4-judge

2023-04-12T00:57:06Z

0xleastwood marked the issue as partial-50

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