Skip to content

Instantly share code, notes, and snippets.

@trust1995
Created January 15, 2025 08:40
Show Gist options
  • Save trust1995/4555ac2633c554897dc46d7c456b22a2 to your computer and use it in GitHub Desktop.
Save trust1995/4555ac2633c554897dc46d7c456b22a2 to your computer and use it in GitHub Desktop.
Bug Bounty Report - Polymarket Oracle DoS

Intro

The UmaCtfAdapter contract is responsible for querying the uma oracle, which will be used to resolve the result of a market. When a new question is asked, it is saved into state + requested from the oracle in the initialize() function. The important line for this issue is the initialization of the conditional tokens condition. This makes sure the payout array is empty, and then sets it to the appropriate length, activating the condition.

    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);
    }

The Bug

As can be seen, the prepareCondition() function is external and publicly accessible. It builds the conditionId only from the input variables. This means an attacker can replicate the parameters from another transaction (like the umaAdapter initialize()) and front-run it. After setting up the condition, the original transaction will fail, because as mentioned, it checks the array is empty. This means that, although the condition is set up, all state management of the question in the adapter cannot take place.

Impact

All new questions using the standard uma adapter can be blocked repeatedly, eventually causing Polymarket to stagnate and only have old questions.

Remediation

Before calling prepareCondition(), the adapter could simple check if it exists by building the conditionId and querying the ConditionalTokens. If it exists, it can skip preparation.

Note on private mempools

A downgrading argument could be that the questions could bypass the polygon mempool and go to a private mempool like Polygon fastlane. This is unsatisfactory for several reasons:

  1. Private mempools are centralized entities and add a layer of dependency.
  2. They cannot be counted on as someone from the mempool can by themself frontrun the TX.
  3. Factors outside of the execution in the EVM are not "fair game" as remediation, as they can be easily abused. The asset in scope is the smart contract, not the entire backend infrastructure.
  4. At a minimum, the exploit would cause distress and delay until the theoretical private mempool would execute it.

Without having prior knowledge of the issue, there would be a minimum downtime until the root cause is established and fixed by a privileged account.

From a PR perspective, it is clear "living alongside" the issue and hoping for the best, instead of fixing it at the source, is a bad take and can be used to showcase Polymarket is not committed to its security. Imagine a bot running 24/7 and tweeting every time Polymarket got frontrun.

Bounty Reward

We believe the bounty is certainly a real DoS risk to Polymarket, in addition to the major reputational risk attached. Considering Polymarket's success and the TVL it contains, these risks in our opinion easily exceed 7-figures of lost value. Therefore, a responsible disclosure of the details of this report merit a bounty at least within the $15-25k range.

Transparency above all else

Our policy as a bug bounty team is to hold all parties accountable for their actions - praising projects which show their dedication to security, while highlighting projects that fail their users by not fixing issues or paying less than what's considered a fair bounty. We believe leveraging our wide audience on X and the blog is one of the only ways to achieve fairness and to ensure projects do not abuse their position of power.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment