Open hats-bug-reporter[bot] opened 2 months ago
prepareCondition is called immediately when the market is created, there's no chance for anyone to frontrun anything
I'm unsure what you mean. This function is external and anyone can call, effectively setting the payoutNumerators[conditionId]
to an incorrect length. prepareCondition
is called when the market is created, yes, but an attacker supplying the incorrect value beforehand (by frontrunning) will taint the entire market from functioning correctly.
function prepareCondition(address oracle, bytes32 questionId, uint outcomeSlotCount) external {
// Limit of 256 because we use a partition array that is a number of 256 bits.
require(outcomeSlotCount <= 256, "too many outcome slots");
require(outcomeSlotCount > 1, "there should be more than one outcome slot");
bytes32 conditionId = CTHelpers.getConditionId(oracle, questionId, outcomeSlotCount);
require(payoutNumerators[conditionId].length == 0, "condition already prepared");
payoutNumerators[conditionId] = new uint[](outcomeSlotCount);
emit ConditionPreparation(conditionId, oracle, questionId, outcomeSlotCount);
}
User wants to create a prediction market with 8 choices. Malicious user frontruns and sets the outcomeSlotCount = 2
here we will have two different conditionIds => two different markets because the outcomeSlotCount
is used to generate the conditionId
, see https://github.com/gnosis/conditional-tokens-contracts/blob/master/contracts/CTHelpers.sol#L11
Yeah, as @xyzseer pointed out, the outcomeSlotCount
is used in the hash determining the conditionId
, so a different outcomeSlotCount
will lead to a different conditionId
.
Github username: -- Twitter username: -- Submission hash (on-chain): 0xaab2011cc37a3989b965518d2a174c1198d2d80ce41c8925140b3bbff5cd941d Severity: high
Description: Description\ Markets can be maliciously misconfigured through
conditionalToken.prepareCondition()
.Users can create a market through various functions such as
createCategoricalMarket()
,createMultiCategoricalMarket()
,createScalarMarket()
, andcreateMultiScalarMarket()
. These functions call the internalcreateMarket()
function which calls which eventually callsprepareCondition()
.This function checks if the condition has already been prepared by checking if the
outcomeSlotCount == 0
. If it is 0, it must be prepared by callingconditionalTokens.prepareCondition()
with the correct parameters, notablyoutcomeSlotCount
.However,
conditionalTokens.prepareCondition()
is external without access control. Anyone can call this function and supplyaddress(realityProxy)
and the samequestionId
with a false value foroutcomeSlotCount
.This will result in some market choices not being able to be redeemed. If the attacker sets the
outcomeSlotCount
to 2 while the market is initialized with many more choices, the higher index choices will not be able to receive payouts.The above
reportPayouts()
function will revert if the caller attempts to report a payout for an array length longer than the length set by the malicious user.Attachments
Proof of Concept (PoC) File
outcomeSlotCount = 2
conditionalTokens.reportPayouts()
expects apayouts
array of length 2.Revised Code File (Optional)
outcomeSlotCount
is already set for the questionId, it is the correct amount. If not, revert. Though this does open up to a griefing bug where a malicious user could cause all market creatiions to revert though it mitigates the fund loss issue.