burnProfile and blockProfile both delete profileToToken and _profiles but never touch the likes mapping in LikeRegistry. A like is a permanent one-way record:
The mutual-like check reads this stale entry directly:
This creates the following exploit path, enabled by M-01 (free re-mint after burn):
User A likes User B and pays 1 ETH.
User A burns their profile.
User A re-mints a fresh profile (M-01).
User B likes User A's new profile.
likes[A][B] is still true from step 1 — a match fires immediately.
User A matched without ever liking User B from their new identity.
The mutual-like invariant is broken. A user can engineer a match without genuinely expressing interest from their current profile — the ghost like from a discarded identity does the work. This undermines the core consent model of the matching system. Combined with M-01, the cost to an attacker is only a burn (free) and a re-mint (gas only), and they arrive in a match with whoever they liked before burning.
Test: testPoC_GhostLikePersistsAfterBurn in test/testLikeRegistry.t.sol
Run with: forge test --match-test testPoC_GhostLikePersistsAfterBurn -vvv
Clear the sender's outgoing likes in burnProfile and blockProfile. Because the likes mapping is keyed by address pairs, the contract cannot enumerate all addresses a user has liked — maintain a separate likedAddresses array per user to support cleanup:
Alternatively, include a profileVersion nonce per address in the like key so that a re-mint automatically invalidates all prior likes without requiring iteration.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.