Open hats-bug-reporter[bot] opened 4 hours ago
I think it'd be easier to discuss this with the concrete example.
It is possible for multiple markets to reference the same question. The question will only need to be answered once. Can you point to a specific issue with that? (providing some example with different actors calling different functions and the resulting issue).
Github username: @J4X_98 Twitter username: J4X_Security Submission hash (on-chain): 0x44135b783d69a9e687774608b7a95fe80f37d846068dc807d9fcd19ee8603743 Severity: medium
Description: Description:
The
createMultiScalarMarket()
function creates a multi-scalar market using theCreateMarketParams
struct, including several arbitrary user-provided strings. It encodes market questions by concatenating thequestionStart
, each outcome, andquestionEnd
strings usingabi.encodePacked()
, which can lead to hash collisions when distinct inputs produce the same encoded result. This causes theaskRealityQuestion()
function to potentially reuse a previously generated question ID if the concatenated string hash matches one from an existing market, even if the question data differs. Although the market name is correctly separated to avoid collisions, this issue with encoded questions can lead to unintended reuse of question IDs across markets.Attack Scenario:
When a user wants to create a multi-scalar market, he calls
createMultiScalarMarket()
, providing theCreateMarketParams
struct.This structure includes multiple strings, which can be of arbitrary size and are user-provided.
As we can see, the
encodedQuestions
are filled out by calling theencodeRealityQuestionWithoutOutcomes()
with some user-provided data. Here the bug occurs, we are usingabi.encodePacked()
to packparams.questionStart + params.outcomes[i] + params.questionEnd
. The problem is that each of these is an arbitrary string of size. So even markets where all 3 are different from one another could end up with the exact string being parsed into theencodeRealityQuestionWithoutOutcomes()
function as thequestion
parameter here.encodeRealityQuestionWithOutcomes()
will then encode this and save it into theencodeQuestions
variable. Later, we can see thataskRealityQuestion()
gets called with the created string.The problem is that if an Id for this string was already generated, we will reuse the same Id.
So, in our case, although this question combination has not been asked yet, we will just use the ID from the already-asked one with the same hash. When we look at how the market name is created later on, we can see that it is correctly separated to prevent collisions.
Recommendation:
The issue can be mitigated by separating the question start, outcome, and question end. This can be achieved either by using abiEncode or by separating them with something like colons.