Reproduced Exploit

Majority Protocol — free cross-game participation via unbound questionId

commitReaction only checks the caller joined _gameId, then forwards an arbitrary _questionId to the prompt strategy. Reactions are stored by questionId alone, so a free-game member can answer paid-game questions and claim winnings without paying that game's fee.

Jan 2026Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Jan 2026

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 65374-users-can-participate-in-an-infinite-number-of-games-they-ha. Standalone Foundry PoC and full write-up: 65374-users-can-participate-in-an-infinite-number-of-games-they-ha_exp in the evm-hack-registry mirror.


Reproduction: self-contained Foundry PoC (forge-std only) — no fork. Full trace: output.txt.

AuditVault taxonomy: lang/solidity · platform/cyfrin · sector/gaming · severity/high · genome: broken-logic · reward-theft · timestamp-dependence


Key info#

ImpactHIGH — join one free game, play infinite paid games without entry fees, still win and claim prizes
ProtocolMajority Protocol
Bug classMissing gameId↔questionId binding in commitReaction
FindingCyfrin (Dacian) · #65374
Reporthttps://github.com/solodit/solodit_content/blob/main/reports/Cyfrin/2026-01-27-cyfrin-majority-protocol-v2.0.md
SourceAuditVault
StatusAudit finding — reproduced as a standalone local synthetic
Compiler^0.8.24 (PoC)

TL;DR#

commitReaction only checks the caller joined _gameId, then forwards an arbitrary _questionId to the prompt strategy. Reactions are stored by questionId alone, so a free-game member can answer paid-game questions and claim winnings without paying that game's fee.

HARM: free-rider claims the paid game's entire prize pool (10 USDC).


Root cause#

No validation that _questionId belongs to _gameId in SessionManager or the strategy.

Preconditions#

At least one free/cheap game the attacker can join, and a funded paid game with a revealed question.

Attack walkthrough#

  1. Create free game + paid game; victim funds paid game.
  2. Attacker joins free game only.
  3. Attacker commitReaction(freeGameId, paidQuestionId, commit) succeeds.
  4. Attacker is ranked winner of the paid game and claims the pool.

Diagrams#

sequenceDiagram participant A as FreeRider participant SM as SessionManager participant P as PromptStrategy participant Pool as PaidGamePool A->>SM: joinGame free only A->>SM: commitReaction freeId paidQ SM->>P: commitReaction no game bind Note over P: reaction keyed by questionId A->>SM: claimRewards paid game SM-->>A: prize pool drained

Impact#

Permanent bypass of all entry fees while remaining eligible to win and claim.

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.