claim() bricks the contract after the first claimA critical logic vulnerability exists in the TreasureHunt::claim function. The contract fails to correctly verify the claim status of specific treasures because it references an uninitialized immutable state variable instead of the function's input parameter.
In the contract state, _treasureHash is declared as an immutable variable but is never assigned a value in the constructor:
Inside the claim function, the validation check is implemented as follows:
The issue is that the contract consistently checks the mapping for the default value 0x0 (the uninitialized state of _treasureHash), completely ignoring the unique treasureHash provided by the user in the function arguments.
Likelihood: High. The bug is triggered by the normal and intended use of the contract. The very first claim will likely pollute the state check for all subsequent users.
Impact: High. 90% of the protocol's funds (90 ETH) become inaccessible. The core business logic of the "Treasure Hunt" is destroyed as no further rewards can be distributed, leading to a complete failure of the smart contract's purpose.
This is a High severity issue as it leads to a permanent Denial of Service (DoS) of the protocol's core functionality after a single successful claim.
Once the first user successfully claims a treasure, the logic effectively "pollutes" the global claim check.
Because the contract checks the static _treasureHash (0x0) rather than the input parameter, every subsequent attempt to claim any other treasure will trigger the AlreadyClaimed revert.
This "bricks" the contract, making it impossible to distribute the remaining 9 rewards. Consequently, 90 ETH (90% of the total rewards) remains locked in the contract, defeating the purpose of the treasure hunt and the protocol's economic incentives.
To verify this vulnerability, follow these steps:
Go into test/TreasureHuntPoC.t.sol.
Paste the following code into the file. This test simulates two different users trying to claim two different treasures.
Solidity
Run the test using the following command in your terminal:
Bash
The test will pass, confirming that the second claim reverts when it should have succeeded.
Update the validation logic to use thetreasureHash parameter provided in the function call:
The issue is that the contract consistently checks the mapping for the default value 0x0 (the uninitialized state of _treasureHash), completely ignoring the unique treasureHash provided by the user in the function arguments.
The bug in `claim()` does not corrupt contract state or brick future claims; it simply checks the wrong mapping key when enforcing uniqueness. The problematic line reads `if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);`, where `_treasureHash` is a separate immutable state variable, while the actual state update later uses the caller-supplied argument through `_markClaimed(treasureHash)`, which sets `claimed[treasureHash] = true`. Since the read and the write are performed against different keys, the effect is a broken duplicate-claim guard, not a destructive state transition. After one treasure is claimed, the mapping entry for that specific `treasureHash` is updated correctly, and other participants can still claim different treasures because the contract has not globally invalidated the claimed mapping or otherwise poisoned shared state. The real consequence of the bug is that previously claimed treasures are not properly blocked from being claimed again, not that the contract becomes unusable for everyone else.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.