Blind Enumeration of Private Card Names via Sort Oracle and ID Discovery

Disclosed by
BobAshEf
  • Engagement Trello
  • Disclosed date about 2 months ago
  • Points 5
  • Priority P4 Bugcrowd's VRT priority rating
  • Status Resolved This vulnerability has been accepted and fixed
Summary by Trello

Privilege Escalation Vulnerability in Trello

Summary by BobAshEf

A chained broken-access-control flaw let a non-privileged attacker enumerate titles of private Trello cards they couldn't view.
Sort oracle: Card content is access-controlled, but "Sort by → Card name (alphabetically)" still sorts unauthorized "mirror cards" into their correct position — enforcing authorization on the read path but not the sort path. By inserting probe cards and observing whether the target sorts before or after each, an attacker can binary-search the alphabet and reconstruct the hidden title without reading it. GraphQL aliasing on updateCardName batches ~50 probes per request; extraction is case-insensitive (MongoDB collation).
ID discovery: Short URLs use only 8 chars of [A-Za-z0-9]. Unauthenticated requests distinguish real cards ("unauthorized") from fake ("not found"), aren't rate-limited, and /batch accepts ~350 sub-requests vs its documented 10 — so valid IDs can be harvested at scale by guessing.
Impact: Chained, these enable mass enumeration of private card titles, privilege escalation, revocation bypass, and real-time title monitoring. Severity depends on what users store in titles.

Report details
  • Submitted

  • Target Location

    trello.com
  • Target category

    Web App

  • VRT

    Broken Access Control (BAC) > Privilege Escalation
  • Priority

    P4
  • Bug URL
    Empty
  • Description

    Sort oracle Vulnerability

    This vulnerability allows non-privileged attacker to enumerate card names of any card he have its id.
    Using a sort oracle in the list sort alphabetically functionality.

    • Now I use ASCII alphabet, but the alphabet could be extended with caution(because the sort is collation sort in mongodb) to cover more.

    Using mirror cards
    Normally if you paste a card url id from another board you do NOT have access to, then it will appear as a mirror card but unauthorized.

    • Here I have copied a link of my boards from another account into this account("Attacker") the card name is "abc".
    • After clicking list options(three dots) => then clicking "Sort by.." => then clicking "Card name (alphabetically)".
    • You can see that even the unauthorized mirrored card is sorted correctly, allowing an attacker to construct its full name. mirror-cards-sort-oracle.png

    Full steps to reproduce

    We need an account for "attacker" and another account for victim(just to get card ids).

    Signup steps for attacker and victim accounts

    1-Go to https://trello.com/ to make a new account, click the login button .
    signup-step1.png
    2-Then click Create account below
    singup-step2.png
    3-Then Enter the email for your account
    4-Then After it will ask you if you really want to create account, click yes.
    5-It will go with you on an onboarding page, click next button.
    6-Then it will ask for account verification via email.
    7-Verify your email, it will ask you to enter your name and password.

    Victim steps

    1-Signup an account for the victim using the steps above with name(ex. Bob Victim).
    2-Then your account will open on your default board
    3-Add any list, then add any card(make the name ASCII, ex. "Top Secret")
    4-Then click to open the card, then clicking "three dots" then "share", then you can see the card id (copy this as we will use it in the python script)
    victim-card-url.png

    The Attacker steps

    1-Signup an account for the victim using the steps above with name(ex. Bob attacker).
    2-Then your account will open on your default board
    3-Now open burp suit, intercept any request with cookie key "cloud.session.token" (copy this as we will use it in the python script to enumerate the card name).
    attacker-session-token.png
    4-Download the python-poc from the submission attachments.
    5-Open config.json file, then paste the attacker "cloud.session.token" and paste the card url
    config.png
    6-Run the script sort-oracle.py, execute these commands in order inside the poc folder

    # skip those if done before
    python3 -m venv venv
    source venv/bin/activate
    pip install -r requirements.txt
    
    # run this(make sure it runs in venv)
    python ./sort-oracle.py
    

    Watch as the card name being enumerated.

    A full poc-video of the steps.

    sort-oracle-poc.mp4

    Output

    card-name-enumeration-output-sample.png

    The script could enumerate more than one card name in parallel, it utilize graphql updateCardName functionality with Aliasing to batch updates in a single request.


    ID Discovery

    Facts

    • Trello uses 8 alphabets and numbers [A-Za-z0-9] in the construction of any card "short url", like this "https://trello.com/c/qMw1aaaa"
    • These short urls are low-entropy compared to the number of cards Trello has.
    • When a non-authenticated user (without a cookie) try to get any card content, the response is "unauthorized" if the card exists and the response is "not found" if the card id does not exist.
    • if the user is not authenticated (without a cookie), the rate-limit does NOT apply.
    • The batch endpoint in Trello REST api says that it accepts maximum of 10 urls, but for some reason this is NOT the case, it can accept as many as the request url accepts.

    Combining these facts, multiple random card-ids could be batched in single request, then by checking the response of each an attacker could easily get random card ids by pure chance

    POC

    1-Download the python-poc from the submission attachments
    2-Run the script find_rnd_card_short_links.py, execute these commands in order inside the poc folder

    # skip those if done before
    python3 -m venv venv
    source venv/bin/activate
    pip install -r requirements.txt
    
    # run this(make sure it runs in venv)
    python ./find_rnd_card_short_links.py
    

    Watch as it prints the valid card ids
    card-id-gen-output-sample.png

    • Note : the script does not use any tokens, it works as non-authenticated user.

    Impact

    Using the sort oracle the impact is dependent on
    1-The sources the attacker could obtain card ids from.
    2-The data the user store in card name.

    Chaining random card ids generator with the sort oracle valn, an attacker could enumerate multiple card names at once, and with parallelism the attacker could breach multiple private card names searching for other users sensitive data.

    Bypassing Access Control

    • Privilege Escalation: If a "Private" card is linked inside a "Team" board, its ID is exposed. This vulnerability allows unauthorized members to read the private title via the link, bypassing ACLs.
    • Bypassing Revocation: Removed members who retain Card IDs can continue monitoring Card Titles indefinitely, nullifying the "Remove Member" security feature for card names.

    Real-Time

    • This exploit allows for continuous monitoring of Card IDs to capture changing sensitive data in card names(for example if a card is known to change name to another sensitive info after some time, a revoked user could store the card id and enumerate it multiple times).

    Other Flaws
    1-Batching Multiplier (ID Discovery): The /batch endpoint accepts up to 350 sub-requests in a single HTTP transaction. This allows an attacker to probe a lot of Card IDs per minute in single request without rate-limiting.
    2-GraphQL Aliasing (Extraction Speed): The GraphQL endpoint allows for aliased queries (checking 50 characters in one mutation). This allowed parallel card names extraction in python-poc(single request to update and single request to sort).


    Notes

    • Now I use ASCII alphabet, but the alphabet could be extended with caution(because the sort is collation sort in mongodb) to cover more.
    • To comply with the program's policy, I have strictly tested this against my own accounts and cards. No customer data was accessed or enumerated.
    • The attached Proof of Concept (PoC) utilizes a binary-search optimization to extract characters with the minimum number of requests.
    • The sort mechanism appears to rely on MongoDB's collation sort, which is case-insensitive. Consequently, the current PoC extracts the text in a case-insensitive manner (e.g., "Pass" and "pass" sort identically).
    • The PoC script is intentionally rate-limited to prevent server strain, but the lack of strict server-side complexity limits allows for significant parallelization.

    • If further demonstration is needed, please let me know.

Activity